@cj-tech-master/excelts 9.5.4 → 9.5.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/browser/modules/archive/compression/streaming-compress.browser.js +29 -0
- package/dist/browser/modules/archive/compression/streaming-compress.js +9 -0
- package/dist/browser/modules/archive/compression/worker-pool/pool.browser.js +26 -1
- package/dist/browser/modules/archive/fs/archive-file.d.ts +8 -5
- package/dist/browser/modules/archive/fs/archive-file.js +78 -16
- package/dist/browser/modules/archive/unzip/stream.browser.js +43 -2
- package/dist/browser/modules/excel/chart/chart-ex-builder.js +7 -2
- package/dist/browser/modules/excel/chart/chart-ex-renderer.js +4 -9
- package/dist/browser/modules/excel/chart/chart-ex-types.d.ts +0 -12
- package/dist/browser/modules/excel/chart/chart.d.ts +1 -5
- package/dist/browser/modules/excel/chart/chart.js +1 -7
- package/dist/browser/modules/excel/chart/types.d.ts +0 -6
- package/dist/browser/modules/excel/stream/workbook-reader.browser.js +25 -1
- package/dist/browser/modules/excel/stream/workbook-reader.js +9 -0
- package/dist/browser/modules/excel/stream/workbook-writer.browser.d.ts +40 -0
- package/dist/browser/modules/excel/stream/workbook-writer.browser.js +228 -13
- package/dist/browser/modules/excel/utils/string-buf.d.ts +5 -26
- package/dist/browser/modules/excel/utils/string-buf.js +4 -81
- package/dist/browser/modules/excel/workbook.browser.js +135 -25
- package/dist/browser/modules/excel/xlsx/xform/chart/chart-space-xform.js +6 -20
- package/dist/browser/modules/excel/xlsx/xlsx.browser.d.ts +19 -9
- package/dist/browser/modules/excel/xlsx/xlsx.browser.js +32 -8
- package/dist/browser/modules/excel/xlsx/xlsx.d.ts +10 -2
- package/dist/browser/modules/excel/xlsx/xlsx.js +9 -1
- package/dist/browser/modules/pdf/excel-bridge.d.ts +30 -1
- package/dist/browser/modules/pdf/excel-bridge.js +32 -0
- package/dist/browser/modules/pdf/font/metrics.d.ts +3 -52
- package/dist/browser/modules/pdf/font/metrics.js +3 -237
- package/dist/browser/modules/pdf/index.d.ts +1 -1
- package/dist/browser/modules/pdf/index.js +1 -1
- package/dist/browser/modules/pdf/render-layout-to-pdf.d.ts +66 -0
- package/dist/browser/modules/pdf/render-layout-to-pdf.js +647 -0
- package/dist/browser/modules/pdf/word-bridge.d.ts +80 -12
- package/dist/browser/modules/pdf/word-bridge.js +122 -274
- package/dist/browser/modules/stream/index.base.d.ts +2 -0
- package/dist/browser/modules/stream/index.base.js +2 -1
- package/dist/browser/modules/stream/internal/sink-adapter.d.ts +65 -0
- package/dist/browser/modules/stream/internal/sink-adapter.js +198 -0
- package/dist/browser/modules/stream/pull-stream.d.ts +19 -2
- package/dist/browser/modules/stream/pull-stream.js +51 -5
- package/dist/browser/modules/stream/types.d.ts +13 -1
- package/dist/browser/modules/word/advanced/diff.d.ts +61 -0
- package/dist/browser/modules/word/advanced/diff.js +167 -0
- package/dist/browser/modules/word/advanced/drawing-shapes.d.ts +269 -0
- package/dist/browser/modules/word/advanced/drawing-shapes.js +268 -0
- package/dist/browser/modules/word/advanced/field-engine.d.ts +43 -0
- package/dist/browser/modules/word/advanced/field-engine.js +1225 -0
- package/dist/browser/modules/word/advanced/glossary.d.ts +86 -0
- package/dist/browser/modules/word/advanced/glossary.js +79 -0
- package/dist/browser/modules/word/advanced/math-convert.d.ts +30 -0
- package/dist/browser/modules/word/advanced/math-convert.js +595 -0
- package/dist/browser/modules/word/advanced/ole-objects.d.ts +115 -0
- package/dist/browser/modules/word/advanced/ole-objects.js +271 -0
- package/dist/browser/modules/word/advanced/style-map.d.ts +105 -0
- package/dist/browser/modules/word/advanced/style-map.js +322 -0
- package/dist/browser/modules/word/advanced/validation.d.ts +56 -0
- package/dist/browser/modules/word/advanced/validation.js +1065 -0
- package/dist/browser/modules/word/advanced/vba-project.d.ts +91 -0
- package/dist/browser/modules/word/advanced/vba-project.js +265 -0
- package/dist/browser/modules/word/bridge/excel-bridge.d.ts +127 -0
- package/dist/browser/modules/word/bridge/excel-bridge.js +980 -0
- package/dist/browser/modules/word/builder/document-handle.d.ts +151 -0
- package/dist/browser/modules/word/builder/document-handle.js +664 -0
- package/dist/browser/modules/word/builder/paragraph-builders.d.ts +61 -0
- package/dist/browser/modules/word/builder/paragraph-builders.js +90 -0
- package/dist/browser/modules/word/builder/run-builders.d.ts +374 -0
- package/dist/browser/modules/word/builder/run-builders.js +600 -0
- package/dist/browser/modules/word/builder/table-builders.d.ts +23 -0
- package/dist/browser/modules/word/builder/table-builders.js +45 -0
- package/dist/browser/modules/word/constants.d.ts +39 -1
- package/dist/browser/modules/word/constants.js +109 -1
- package/dist/browser/modules/word/convert/conversion-ir.d.ts +210 -0
- package/dist/browser/modules/word/convert/conversion-ir.js +31 -0
- package/dist/browser/modules/word/convert/docx-to-semantic.d.ts +39 -0
- package/dist/browser/modules/word/convert/docx-to-semantic.js +499 -0
- package/dist/browser/modules/word/convert/flat-opc.d.ts +44 -0
- package/dist/browser/modules/word/convert/flat-opc.js +385 -0
- package/dist/browser/modules/word/convert/html/html-import.d.ts +50 -0
- package/dist/browser/modules/word/convert/html/html-import.js +1907 -0
- package/dist/{types/modules/word → browser/modules/word/convert/html}/html-renderer.d.ts +14 -1
- package/dist/{esm/modules/word → browser/modules/word/convert/html}/html-renderer.js +420 -69
- package/dist/browser/modules/word/convert/html/html.d.ts +15 -0
- package/dist/browser/modules/word/convert/html/html.js +15 -0
- package/dist/browser/modules/word/convert/markdown/markdown-import.d.ts +68 -0
- package/dist/browser/modules/word/convert/markdown/markdown-import.js +1325 -0
- package/dist/browser/modules/word/convert/markdown/markdown-renderer.d.ts +25 -0
- package/dist/browser/modules/word/convert/markdown/markdown-renderer.js +634 -0
- package/dist/browser/modules/word/convert/markdown/markdown.d.ts +15 -0
- package/dist/browser/modules/word/convert/markdown/markdown.js +15 -0
- package/dist/browser/modules/word/convert/odt/odt.d.ts +41 -0
- package/dist/browser/modules/word/convert/odt/odt.js +1932 -0
- package/dist/browser/modules/word/{color-utils.d.ts → core/color-utils.d.ts} +8 -1
- package/dist/browser/modules/word/core/color-utils.js +43 -0
- package/dist/browser/modules/word/core/internal-utils.d.ts +90 -0
- package/dist/browser/modules/word/core/internal-utils.js +209 -0
- package/dist/browser/modules/word/core/mapper.d.ts +44 -0
- package/dist/browser/modules/word/core/mapper.js +427 -0
- package/dist/browser/modules/word/core/opc-paths.d.ts +33 -0
- package/dist/browser/modules/word/core/opc-paths.js +48 -0
- package/dist/browser/modules/word/core/text-utils.d.ts +38 -0
- package/dist/browser/modules/word/core/text-utils.js +202 -0
- package/dist/browser/modules/word/core/walker.d.ts +119 -0
- package/dist/browser/modules/word/core/walker.js +570 -0
- package/dist/browser/modules/word/crypto.d.ts +14 -9
- package/dist/browser/modules/word/crypto.js +13 -7
- package/dist/browser/modules/word/document-io.d.ts +59 -27
- package/dist/browser/modules/word/document-io.js +80 -197
- package/dist/browser/modules/word/errors.d.ts +44 -1
- package/dist/browser/modules/word/errors.js +54 -2
- package/dist/browser/modules/word/excel.d.ts +14 -0
- package/dist/browser/modules/word/excel.js +13 -0
- package/dist/browser/modules/word/font/font-embed.d.ts +112 -0
- package/dist/browser/modules/word/font/font-embed.js +646 -0
- package/dist/{esm/modules/word → browser/modules/word/font}/font-obfuscation.js +4 -9
- package/dist/browser/modules/word/font/hyphenation.d.ts +65 -0
- package/dist/browser/modules/word/font/hyphenation.js +4210 -0
- package/dist/browser/modules/word/font/text-shaping.d.ts +58 -0
- package/dist/browser/modules/word/font/text-shaping.js +635 -0
- package/dist/browser/modules/word/html.d.ts +7 -6
- package/dist/browser/modules/word/html.js +6 -5
- package/dist/browser/modules/word/incremental-edit.d.ts +123 -0
- package/dist/browser/modules/word/incremental-edit.js +361 -0
- package/dist/browser/modules/word/index.base.d.ts +194 -10
- package/dist/browser/modules/word/index.base.js +138 -29
- package/dist/browser/modules/word/layout/layout-constants.d.ts +17 -0
- package/dist/browser/modules/word/layout/layout-constants.js +17 -0
- package/dist/browser/modules/word/layout/layout-full.d.ts +53 -0
- package/dist/browser/modules/word/layout/layout-full.js +1696 -0
- package/dist/browser/modules/word/layout/layout-model.d.ts +344 -0
- package/dist/browser/modules/word/layout/layout-model.js +16 -0
- package/dist/browser/modules/word/layout/layout.d.ts +63 -0
- package/dist/browser/modules/word/layout/layout.js +1167 -0
- package/dist/browser/modules/word/layout/render-page.d.ts +57 -0
- package/dist/browser/modules/word/layout/render-page.js +1238 -0
- package/dist/browser/modules/word/markdown.d.ts +14 -0
- package/dist/browser/modules/word/markdown.js +13 -0
- package/dist/browser/modules/word/patcher.d.ts +62 -0
- package/dist/browser/modules/word/patcher.js +537 -0
- package/dist/browser/modules/word/query/compat.d.ts +25 -0
- package/dist/browser/modules/word/query/compat.js +58 -0
- package/dist/browser/modules/word/query/data-binding.d.ts +22 -0
- package/dist/browser/modules/word/query/data-binding.js +392 -0
- package/dist/browser/modules/word/query/form-fields.d.ts +41 -0
- package/dist/browser/modules/word/query/form-fields.js +268 -0
- package/dist/browser/modules/word/query/format-search.d.ts +99 -0
- package/dist/browser/modules/word/query/format-search.js +329 -0
- package/dist/browser/modules/word/query/mail-merge.d.ts +25 -0
- package/dist/browser/modules/word/query/mail-merge.js +111 -0
- package/dist/browser/modules/word/query/merge.d.ts +50 -0
- package/dist/browser/modules/word/query/merge.js +617 -0
- package/dist/browser/modules/word/query/replace.d.ts +47 -0
- package/dist/browser/modules/word/query/replace.js +301 -0
- package/dist/browser/modules/word/query/revisions.d.ts +67 -0
- package/dist/browser/modules/word/query/revisions.js +879 -0
- package/dist/browser/modules/word/query/search.d.ts +129 -0
- package/dist/browser/modules/word/query/search.js +346 -0
- package/dist/browser/modules/word/query/split.d.ts +44 -0
- package/dist/browser/modules/word/query/split.js +135 -0
- package/dist/browser/modules/word/query/style-resolve.d.ts +104 -0
- package/dist/browser/modules/word/query/style-resolve.js +368 -0
- package/dist/browser/modules/word/reader/chart-parser.d.ts +20 -0
- package/dist/browser/modules/word/reader/chart-parser.js +810 -0
- package/dist/browser/modules/word/reader/comments-parser.d.ts +26 -0
- package/dist/browser/modules/word/reader/comments-parser.js +92 -0
- package/dist/browser/modules/word/reader/doc-props-parsers.d.ts +15 -0
- package/dist/browser/modules/word/reader/doc-props-parsers.js +190 -0
- package/dist/browser/modules/word/reader/docx-reader.d.ts +27 -0
- package/dist/browser/modules/word/reader/docx-reader.js +2557 -0
- package/dist/browser/modules/word/reader/drawing-helpers.d.ts +27 -0
- package/dist/browser/modules/word/reader/drawing-helpers.js +84 -0
- package/dist/browser/modules/word/reader/form-field-parser.d.ts +21 -0
- package/dist/browser/modules/word/reader/form-field-parser.js +82 -0
- package/dist/browser/modules/word/reader/image-parsers.d.ts +11 -0
- package/dist/browser/modules/word/reader/image-parsers.js +291 -0
- package/dist/browser/modules/word/reader/math-parser.d.ts +12 -0
- package/dist/browser/modules/word/reader/math-parser.js +422 -0
- package/dist/browser/modules/word/reader/metadata-parsers.d.ts +17 -0
- package/dist/browser/modules/word/reader/metadata-parsers.js +87 -0
- package/dist/browser/modules/word/reader/numbering-parser.d.ts +13 -0
- package/dist/browser/modules/word/reader/numbering-parser.js +166 -0
- package/dist/browser/modules/word/reader/paragraph-section-parsers.d.ts +12 -0
- package/dist/browser/modules/word/reader/paragraph-section-parsers.js +503 -0
- package/dist/browser/modules/word/reader/parse-utils.d.ts +91 -0
- package/dist/browser/modules/word/reader/parse-utils.js +249 -0
- package/dist/browser/modules/word/reader/properties-parsers.d.ts +21 -0
- package/dist/browser/modules/word/reader/properties-parsers.js +332 -0
- package/dist/browser/modules/word/reader/reader-context.d.ts +69 -0
- package/dist/browser/modules/word/reader/reader-context.js +61 -0
- package/dist/browser/modules/word/reader/sdt-helpers.d.ts +29 -0
- package/dist/browser/modules/word/reader/sdt-helpers.js +111 -0
- package/dist/browser/modules/word/reader/settings-parser.d.ts +8 -0
- package/dist/browser/modules/word/reader/settings-parser.js +263 -0
- package/dist/browser/modules/word/reader/styles-parser.d.ts +12 -0
- package/dist/browser/modules/word/reader/styles-parser.js +147 -0
- package/dist/browser/modules/word/reader/table-properties-parsers.d.ts +12 -0
- package/dist/browser/modules/word/reader/table-properties-parsers.js +234 -0
- package/dist/browser/modules/word/reader/theme-parser.d.ts +8 -0
- package/dist/browser/modules/word/reader/theme-parser.js +167 -0
- package/dist/browser/modules/word/reader/watermark-parser.d.ts +15 -0
- package/dist/browser/modules/word/reader/watermark-parser.js +110 -0
- package/dist/browser/modules/word/security/cfb-reader.d.ts +37 -0
- package/dist/browser/modules/word/security/cfb-reader.js +410 -0
- package/dist/browser/modules/word/{digital-signatures.d.ts → security/digital-signatures.d.ts} +19 -11
- package/dist/browser/modules/word/{digital-signatures.js → security/digital-signatures.js} +34 -34
- package/dist/browser/modules/word/security/document-protection.d.ts +93 -0
- package/dist/browser/modules/word/security/document-protection.js +201 -0
- package/dist/{types/modules/word → browser/modules/word/security}/encryption.d.ts +51 -4
- package/dist/browser/modules/word/security/encryption.js +602 -0
- package/dist/browser/modules/word/security/policy.d.ts +80 -0
- package/dist/browser/modules/word/security/policy.js +102 -0
- package/dist/browser/modules/word/template/template-chart.d.ts +56 -0
- package/dist/browser/modules/word/template/template-chart.js +167 -0
- package/dist/browser/modules/word/template/template-datasource.d.ts +154 -0
- package/dist/browser/modules/word/template/template-datasource.js +541 -0
- package/dist/browser/modules/word/template/template-engine.d.ts +121 -0
- package/dist/browser/modules/word/template/template-engine.js +1435 -0
- package/dist/browser/modules/word/types.d.ts +224 -25
- package/dist/browser/modules/word/units.d.ts +26 -0
- package/dist/browser/modules/word/units.js +43 -14
- package/dist/browser/modules/word/{writers → writer}/chart-writer.js +164 -23
- package/dist/browser/modules/word/writer/checkbox-writer.d.ts +17 -0
- package/dist/browser/modules/word/writer/checkbox-writer.js +79 -0
- package/dist/{types/modules/word/writers → browser/modules/word/writer}/comment-writer.d.ts +2 -1
- package/dist/browser/modules/word/{writers → writer}/comment-writer.js +8 -6
- package/dist/browser/modules/word/writer/common-parts.d.ts +57 -0
- package/dist/browser/modules/word/writer/common-parts.js +101 -0
- package/dist/{types/modules/word → browser/modules/word/writer}/content-types.d.ts +2 -2
- package/dist/{esm/modules/word → browser/modules/word/writer}/content-types.js +14 -6
- package/dist/browser/modules/word/writer/document-writer.d.ts +24 -0
- package/dist/browser/modules/word/writer/document-writer.js +473 -0
- package/dist/browser/modules/word/writer/docx-packager.d.ts +35 -0
- package/dist/browser/modules/word/writer/docx-packager.js +1515 -0
- package/dist/{types/modules/word/writers → browser/modules/word/writer}/footnote-writer.d.ts +3 -2
- package/dist/{esm/modules/word/writers → browser/modules/word/writer}/footnote-writer.js +13 -10
- package/dist/{types/modules/word/writers → browser/modules/word/writer}/header-footer-writer.d.ts +3 -2
- package/dist/{esm/modules/word/writers → browser/modules/word/writer}/header-footer-writer.js +39 -21
- package/dist/{types/modules/word/writers → browser/modules/word/writer}/image-writer.d.ts +1 -1
- package/dist/browser/modules/word/{writers → writer}/image-writer.js +11 -7
- package/dist/browser/modules/word/writer/math-writer.d.ts +20 -0
- package/dist/{esm/modules/word/writers → browser/modules/word/writer}/math-writer.js +21 -1
- package/dist/browser/modules/word/{writers → writer}/numbering-writer.d.ts +1 -1
- package/dist/{esm/modules/word/writers → browser/modules/word/writer}/numbering-writer.js +11 -4
- package/dist/browser/modules/word/{writers → writer}/paragraph-writer.d.ts +2 -1
- package/dist/browser/modules/word/{writers → writer}/paragraph-writer.js +73 -38
- package/dist/browser/modules/word/{writers → writer}/parts-writer.d.ts +3 -3
- package/dist/{esm/modules/word/writers → browser/modules/word/writer}/parts-writer.js +91 -12
- package/dist/browser/modules/word/writer/reference-scanners.d.ts +42 -0
- package/dist/browser/modules/word/writer/reference-scanners.js +111 -0
- package/dist/browser/modules/word/writer/relationships.d.ts +52 -0
- package/dist/browser/modules/word/writer/relationships.js +117 -0
- package/dist/browser/modules/word/writer/render-context.d.ts +124 -0
- package/dist/browser/modules/word/writer/render-context.js +46 -0
- package/dist/browser/modules/word/{writers → writer}/run-writer.d.ts +10 -1
- package/dist/{esm/modules/word/writers → browser/modules/word/writer}/run-writer.js +126 -24
- package/dist/browser/modules/word/writer/sdt-writer.d.ts +25 -0
- package/dist/browser/modules/word/writer/sdt-writer.js +189 -0
- package/dist/browser/modules/word/writer/stream-buf.d.ts +37 -0
- package/dist/browser/modules/word/writer/stream-buf.js +73 -0
- package/dist/browser/modules/word/writer/streaming-writer.d.ts +344 -0
- package/dist/browser/modules/word/writer/streaming-writer.js +1382 -0
- package/dist/browser/modules/word/writer/string-buf.d.ts +8 -0
- package/dist/browser/modules/word/writer/string-buf.js +7 -0
- package/dist/browser/modules/word/{writers → writer}/styles-writer.js +32 -1
- package/dist/browser/modules/word/{writers → writer}/table-writer.d.ts +2 -1
- package/dist/browser/modules/word/{writers → writer}/table-writer.js +94 -11
- package/dist/browser/modules/xml/types.d.ts +22 -0
- package/dist/browser/utils/crypto.browser.d.ts +3 -1
- package/dist/browser/utils/crypto.browser.js +3 -1
- package/dist/browser/utils/crypto.d.ts +4 -1
- package/dist/browser/utils/crypto.js +4 -1
- package/dist/browser/utils/font-metrics.d.ts +63 -0
- package/dist/browser/utils/font-metrics.js +293 -0
- package/dist/browser/utils/string-buf.d.ts +42 -0
- package/dist/browser/utils/string-buf.js +89 -0
- package/dist/browser/utils/theme-colors.d.ts +55 -0
- package/dist/browser/utils/theme-colors.js +120 -0
- package/dist/cjs/modules/archive/compression/streaming-compress.browser.js +29 -0
- package/dist/cjs/modules/archive/compression/streaming-compress.js +9 -0
- package/dist/cjs/modules/archive/compression/worker-pool/pool.browser.js +26 -1
- package/dist/cjs/modules/archive/fs/archive-file.js +78 -16
- package/dist/cjs/modules/archive/unzip/stream.browser.js +43 -2
- package/dist/cjs/modules/excel/chart/chart-ex-builder.js +7 -2
- package/dist/cjs/modules/excel/chart/chart-ex-renderer.js +4 -9
- package/dist/cjs/modules/excel/chart/chart.js +1 -7
- package/dist/cjs/modules/excel/stream/workbook-reader.browser.js +25 -1
- package/dist/cjs/modules/excel/stream/workbook-reader.js +9 -0
- package/dist/cjs/modules/excel/stream/workbook-writer.browser.js +228 -13
- package/dist/cjs/modules/excel/utils/string-buf.js +5 -81
- package/dist/cjs/modules/excel/workbook.browser.js +135 -25
- package/dist/cjs/modules/excel/xlsx/xform/chart/chart-space-xform.js +6 -20
- package/dist/cjs/modules/excel/xlsx/xlsx.browser.js +32 -8
- package/dist/cjs/modules/excel/xlsx/xlsx.js +9 -1
- package/dist/cjs/modules/pdf/excel-bridge.js +33 -0
- package/dist/cjs/modules/pdf/font/metrics.js +11 -244
- package/dist/cjs/modules/pdf/index.js +2 -1
- package/dist/cjs/modules/pdf/render-layout-to-pdf.js +651 -0
- package/dist/cjs/modules/pdf/word-bridge.js +155 -274
- package/dist/cjs/modules/stream/index.base.js +4 -2
- package/dist/cjs/modules/stream/internal/sink-adapter.js +202 -0
- package/dist/cjs/modules/stream/pull-stream.js +51 -5
- package/dist/cjs/modules/word/advanced/diff.js +170 -0
- package/dist/cjs/modules/word/advanced/drawing-shapes.js +279 -0
- package/dist/cjs/modules/word/advanced/field-engine.js +1229 -0
- package/dist/cjs/modules/word/advanced/glossary.js +87 -0
- package/dist/cjs/modules/word/advanced/math-convert.js +599 -0
- package/dist/cjs/modules/word/advanced/ole-objects.js +277 -0
- package/dist/cjs/modules/word/advanced/style-map.js +329 -0
- package/dist/cjs/modules/word/advanced/validation.js +1068 -0
- package/dist/cjs/modules/word/advanced/vba-project.js +274 -0
- package/dist/cjs/modules/word/bridge/excel-bridge.js +1020 -0
- package/dist/cjs/modules/word/builder/document-handle.js +667 -0
- package/dist/cjs/modules/word/builder/paragraph-builders.js +109 -0
- package/dist/cjs/modules/word/builder/run-builders.js +676 -0
- package/dist/cjs/modules/word/builder/table-builders.js +53 -0
- package/dist/cjs/modules/word/constants.js +111 -2
- package/dist/cjs/modules/word/convert/conversion-ir.js +34 -0
- package/dist/cjs/modules/word/convert/docx-to-semantic.js +502 -0
- package/dist/cjs/modules/word/convert/flat-opc.js +390 -0
- package/dist/cjs/modules/word/convert/html/html-import.js +1910 -0
- package/dist/cjs/modules/word/{html-renderer.js → convert/html/html-renderer.js} +420 -69
- package/dist/cjs/modules/word/convert/html/html.js +20 -0
- package/dist/cjs/modules/word/convert/markdown/markdown-import.js +1329 -0
- package/dist/cjs/modules/word/convert/markdown/markdown-renderer.js +637 -0
- package/dist/cjs/modules/word/convert/markdown/markdown.js +21 -0
- package/dist/cjs/modules/word/convert/odt/odt.js +1936 -0
- package/dist/cjs/modules/word/core/color-utils.js +47 -0
- package/dist/cjs/modules/word/core/internal-utils.js +219 -0
- package/dist/cjs/modules/word/core/mapper.js +430 -0
- package/dist/cjs/modules/word/core/opc-paths.js +53 -0
- package/dist/cjs/modules/word/core/text-utils.js +210 -0
- package/dist/cjs/modules/word/core/walker.js +577 -0
- package/dist/cjs/modules/word/crypto.js +19 -8
- package/dist/cjs/modules/word/document-io.js +117 -197
- package/dist/cjs/modules/word/errors.js +59 -13
- package/dist/cjs/modules/word/excel.js +22 -0
- package/dist/cjs/modules/word/font/font-embed.js +652 -0
- package/dist/cjs/modules/word/{font-obfuscation.js → font/font-obfuscation.js} +4 -9
- package/dist/cjs/modules/word/font/hyphenation.js +4216 -0
- package/dist/cjs/modules/word/font/text-shaping.js +640 -0
- package/dist/cjs/modules/word/html.js +9 -7
- package/dist/cjs/modules/word/incremental-edit.js +366 -0
- package/dist/cjs/modules/word/index.base.js +370 -137
- package/dist/cjs/modules/word/layout/layout-constants.js +20 -0
- package/dist/cjs/modules/word/layout/layout-full.js +1699 -0
- package/dist/cjs/modules/word/layout/layout-model.js +17 -0
- package/dist/cjs/modules/word/layout/layout.js +1170 -0
- package/dist/cjs/modules/word/layout/render-page.js +1243 -0
- package/dist/cjs/modules/word/markdown.js +19 -0
- package/dist/cjs/modules/word/patcher.js +539 -0
- package/dist/cjs/modules/word/query/compat.js +61 -0
- package/dist/cjs/modules/word/query/data-binding.js +395 -0
- package/dist/cjs/modules/word/query/form-fields.js +272 -0
- package/dist/cjs/modules/word/query/format-search.js +334 -0
- package/dist/cjs/modules/word/query/mail-merge.js +114 -0
- package/dist/cjs/modules/word/query/merge.js +620 -0
- package/dist/cjs/modules/word/query/replace.js +304 -0
- package/dist/cjs/modules/word/query/revisions.js +885 -0
- package/dist/cjs/modules/word/query/search.js +361 -0
- package/dist/cjs/modules/word/query/split.js +138 -0
- package/dist/cjs/modules/word/query/style-resolve.js +374 -0
- package/dist/cjs/modules/word/reader/chart-parser.js +814 -0
- package/dist/cjs/modules/word/reader/comments-parser.js +96 -0
- package/dist/cjs/modules/word/reader/doc-props-parsers.js +194 -0
- package/dist/cjs/modules/word/reader/docx-reader.js +2560 -0
- package/dist/cjs/modules/word/reader/drawing-helpers.js +90 -0
- package/dist/cjs/modules/word/reader/form-field-parser.js +85 -0
- package/dist/cjs/modules/word/reader/image-parsers.js +293 -0
- package/dist/cjs/modules/word/reader/math-parser.js +424 -0
- package/dist/cjs/modules/word/reader/metadata-parsers.js +93 -0
- package/dist/cjs/modules/word/reader/numbering-parser.js +168 -0
- package/dist/cjs/modules/word/reader/paragraph-section-parsers.js +505 -0
- package/dist/cjs/modules/word/reader/parse-utils.js +271 -0
- package/dist/cjs/modules/word/reader/properties-parsers.js +338 -0
- package/dist/cjs/modules/word/reader/reader-context.js +66 -0
- package/dist/cjs/modules/word/reader/sdt-helpers.js +114 -0
- package/dist/cjs/modules/word/reader/settings-parser.js +265 -0
- package/dist/cjs/modules/word/reader/styles-parser.js +149 -0
- package/dist/cjs/modules/word/reader/table-properties-parsers.js +237 -0
- package/dist/cjs/modules/word/reader/theme-parser.js +169 -0
- package/dist/cjs/modules/word/reader/watermark-parser.js +113 -0
- package/dist/cjs/modules/word/security/cfb-reader.js +414 -0
- package/dist/cjs/modules/word/{digital-signatures.js → security/digital-signatures.js} +34 -34
- package/dist/cjs/modules/word/security/document-protection.js +208 -0
- package/dist/cjs/modules/word/security/encryption.js +612 -0
- package/dist/cjs/modules/word/security/policy.js +106 -0
- package/dist/cjs/modules/word/template/template-chart.js +170 -0
- package/dist/cjs/modules/word/template/template-datasource.js +549 -0
- package/dist/cjs/modules/word/template/template-engine.js +1430 -0
- package/dist/cjs/modules/word/units.js +44 -14
- package/dist/cjs/modules/word/{writers → writer}/chart-writer.js +163 -22
- package/dist/cjs/modules/word/writer/checkbox-writer.js +82 -0
- package/dist/cjs/modules/word/{writers → writer}/comment-writer.js +8 -6
- package/dist/cjs/modules/word/writer/common-parts.js +104 -0
- package/dist/cjs/modules/word/{content-types.js → writer/content-types.js} +14 -6
- package/dist/cjs/modules/word/writer/document-writer.js +478 -0
- package/dist/cjs/modules/word/writer/docx-packager.js +1551 -0
- package/dist/cjs/modules/word/{writers → writer}/footnote-writer.js +13 -10
- package/dist/cjs/modules/word/{writers → writer}/header-footer-writer.js +38 -20
- package/dist/cjs/modules/word/{writers → writer}/image-writer.js +11 -7
- package/dist/cjs/modules/word/{writers → writer}/math-writer.js +21 -1
- package/dist/cjs/modules/word/{writers → writer}/numbering-writer.js +11 -4
- package/dist/cjs/modules/word/{writers → writer}/paragraph-writer.js +72 -37
- package/dist/cjs/modules/word/{writers → writer}/parts-writer.js +91 -12
- package/dist/cjs/modules/word/writer/reference-scanners.js +120 -0
- package/dist/cjs/modules/word/writer/relationships.js +124 -0
- package/dist/cjs/modules/word/writer/render-context.js +51 -0
- package/dist/cjs/modules/word/{writers → writer}/run-writer.js +127 -24
- package/dist/cjs/modules/word/writer/sdt-writer.js +192 -0
- package/dist/cjs/modules/word/writer/stream-buf.js +76 -0
- package/dist/cjs/modules/word/writer/streaming-writer.js +1387 -0
- package/dist/cjs/modules/word/writer/string-buf.js +11 -0
- package/dist/cjs/modules/word/{writers → writer}/styles-writer.js +32 -1
- package/dist/cjs/modules/word/{writers → writer}/table-writer.js +94 -11
- package/dist/cjs/utils/crypto.browser.js +3 -1
- package/dist/cjs/utils/crypto.js +4 -1
- package/dist/cjs/utils/font-metrics.js +303 -0
- package/dist/cjs/utils/string-buf.js +92 -0
- package/dist/cjs/utils/theme-colors.js +126 -0
- package/dist/esm/modules/archive/compression/streaming-compress.browser.js +29 -0
- package/dist/esm/modules/archive/compression/streaming-compress.js +9 -0
- package/dist/esm/modules/archive/compression/worker-pool/pool.browser.js +26 -1
- package/dist/esm/modules/archive/fs/archive-file.js +78 -16
- package/dist/esm/modules/archive/unzip/stream.browser.js +43 -2
- package/dist/esm/modules/excel/chart/chart-ex-builder.js +7 -2
- package/dist/esm/modules/excel/chart/chart-ex-renderer.js +4 -9
- package/dist/esm/modules/excel/chart/chart.js +1 -7
- package/dist/esm/modules/excel/stream/workbook-reader.browser.js +25 -1
- package/dist/esm/modules/excel/stream/workbook-reader.js +9 -0
- package/dist/esm/modules/excel/stream/workbook-writer.browser.js +228 -13
- package/dist/esm/modules/excel/utils/string-buf.js +4 -81
- package/dist/esm/modules/excel/workbook.browser.js +135 -25
- package/dist/esm/modules/excel/xlsx/xform/chart/chart-space-xform.js +6 -20
- package/dist/esm/modules/excel/xlsx/xlsx.browser.js +32 -8
- package/dist/esm/modules/excel/xlsx/xlsx.js +9 -1
- package/dist/esm/modules/pdf/excel-bridge.js +32 -0
- package/dist/esm/modules/pdf/font/metrics.js +3 -237
- package/dist/esm/modules/pdf/index.js +1 -1
- package/dist/esm/modules/pdf/render-layout-to-pdf.js +647 -0
- package/dist/esm/modules/pdf/word-bridge.js +122 -274
- package/dist/esm/modules/stream/index.base.js +2 -1
- package/dist/esm/modules/stream/internal/sink-adapter.js +198 -0
- package/dist/esm/modules/stream/pull-stream.js +51 -5
- package/dist/esm/modules/word/advanced/diff.js +167 -0
- package/dist/esm/modules/word/advanced/drawing-shapes.js +268 -0
- package/dist/esm/modules/word/advanced/field-engine.js +1225 -0
- package/dist/esm/modules/word/advanced/glossary.js +79 -0
- package/dist/esm/modules/word/advanced/math-convert.js +595 -0
- package/dist/esm/modules/word/advanced/ole-objects.js +271 -0
- package/dist/esm/modules/word/advanced/style-map.js +322 -0
- package/dist/esm/modules/word/advanced/validation.js +1065 -0
- package/dist/esm/modules/word/advanced/vba-project.js +265 -0
- package/dist/esm/modules/word/bridge/excel-bridge.js +980 -0
- package/dist/esm/modules/word/builder/document-handle.js +664 -0
- package/dist/esm/modules/word/builder/paragraph-builders.js +90 -0
- package/dist/esm/modules/word/builder/run-builders.js +600 -0
- package/dist/esm/modules/word/builder/table-builders.js +45 -0
- package/dist/esm/modules/word/constants.js +109 -1
- package/dist/esm/modules/word/convert/conversion-ir.js +31 -0
- package/dist/esm/modules/word/convert/docx-to-semantic.js +499 -0
- package/dist/esm/modules/word/convert/flat-opc.js +385 -0
- package/dist/esm/modules/word/convert/html/html-import.js +1907 -0
- package/dist/{browser/modules/word → esm/modules/word/convert/html}/html-renderer.js +420 -69
- package/dist/esm/modules/word/convert/html/html.js +15 -0
- package/dist/esm/modules/word/convert/markdown/markdown-import.js +1325 -0
- package/dist/esm/modules/word/convert/markdown/markdown-renderer.js +634 -0
- package/dist/esm/modules/word/convert/markdown/markdown.js +15 -0
- package/dist/esm/modules/word/convert/odt/odt.js +1932 -0
- package/dist/esm/modules/word/core/color-utils.js +43 -0
- package/dist/esm/modules/word/core/internal-utils.js +209 -0
- package/dist/esm/modules/word/core/mapper.js +427 -0
- package/dist/esm/modules/word/core/opc-paths.js +48 -0
- package/dist/esm/modules/word/core/text-utils.js +202 -0
- package/dist/esm/modules/word/core/walker.js +570 -0
- package/dist/esm/modules/word/crypto.js +13 -7
- package/dist/esm/modules/word/document-io.js +80 -197
- package/dist/esm/modules/word/errors.js +54 -2
- package/dist/esm/modules/word/excel.js +13 -0
- package/dist/esm/modules/word/font/font-embed.js +646 -0
- package/dist/{browser/modules/word → esm/modules/word/font}/font-obfuscation.js +4 -9
- package/dist/esm/modules/word/font/hyphenation.js +4210 -0
- package/dist/esm/modules/word/font/text-shaping.js +635 -0
- package/dist/esm/modules/word/html.js +6 -5
- package/dist/esm/modules/word/incremental-edit.js +361 -0
- package/dist/esm/modules/word/index.base.js +138 -29
- package/dist/esm/modules/word/layout/layout-constants.js +17 -0
- package/dist/esm/modules/word/layout/layout-full.js +1696 -0
- package/dist/esm/modules/word/layout/layout-model.js +16 -0
- package/dist/esm/modules/word/layout/layout.js +1167 -0
- package/dist/esm/modules/word/layout/render-page.js +1238 -0
- package/dist/esm/modules/word/markdown.js +13 -0
- package/dist/esm/modules/word/patcher.js +537 -0
- package/dist/esm/modules/word/query/compat.js +58 -0
- package/dist/esm/modules/word/query/data-binding.js +392 -0
- package/dist/esm/modules/word/query/form-fields.js +268 -0
- package/dist/esm/modules/word/query/format-search.js +329 -0
- package/dist/esm/modules/word/query/mail-merge.js +111 -0
- package/dist/esm/modules/word/query/merge.js +617 -0
- package/dist/esm/modules/word/query/replace.js +301 -0
- package/dist/esm/modules/word/query/revisions.js +879 -0
- package/dist/esm/modules/word/query/search.js +346 -0
- package/dist/esm/modules/word/query/split.js +135 -0
- package/dist/esm/modules/word/query/style-resolve.js +368 -0
- package/dist/esm/modules/word/reader/chart-parser.js +810 -0
- package/dist/esm/modules/word/reader/comments-parser.js +92 -0
- package/dist/esm/modules/word/reader/doc-props-parsers.js +190 -0
- package/dist/esm/modules/word/reader/docx-reader.js +2557 -0
- package/dist/esm/modules/word/reader/drawing-helpers.js +84 -0
- package/dist/esm/modules/word/reader/form-field-parser.js +82 -0
- package/dist/esm/modules/word/reader/image-parsers.js +291 -0
- package/dist/esm/modules/word/reader/math-parser.js +422 -0
- package/dist/esm/modules/word/reader/metadata-parsers.js +87 -0
- package/dist/esm/modules/word/reader/numbering-parser.js +166 -0
- package/dist/esm/modules/word/reader/paragraph-section-parsers.js +503 -0
- package/dist/esm/modules/word/reader/parse-utils.js +249 -0
- package/dist/esm/modules/word/reader/properties-parsers.js +332 -0
- package/dist/esm/modules/word/reader/reader-context.js +61 -0
- package/dist/esm/modules/word/reader/sdt-helpers.js +111 -0
- package/dist/esm/modules/word/reader/settings-parser.js +263 -0
- package/dist/esm/modules/word/reader/styles-parser.js +147 -0
- package/dist/esm/modules/word/reader/table-properties-parsers.js +234 -0
- package/dist/esm/modules/word/reader/theme-parser.js +167 -0
- package/dist/esm/modules/word/reader/watermark-parser.js +110 -0
- package/dist/esm/modules/word/security/cfb-reader.js +410 -0
- package/dist/esm/modules/word/{digital-signatures.js → security/digital-signatures.js} +34 -34
- package/dist/esm/modules/word/security/document-protection.js +201 -0
- package/dist/esm/modules/word/security/encryption.js +602 -0
- package/dist/esm/modules/word/security/policy.js +102 -0
- package/dist/esm/modules/word/template/template-chart.js +167 -0
- package/dist/esm/modules/word/template/template-datasource.js +541 -0
- package/dist/esm/modules/word/template/template-engine.js +1435 -0
- package/dist/esm/modules/word/units.js +43 -14
- package/dist/esm/modules/word/{writers → writer}/chart-writer.js +164 -23
- package/dist/esm/modules/word/writer/checkbox-writer.js +79 -0
- package/dist/esm/modules/word/{writers → writer}/comment-writer.js +8 -6
- package/dist/esm/modules/word/writer/common-parts.js +101 -0
- package/dist/{browser/modules/word → esm/modules/word/writer}/content-types.js +14 -6
- package/dist/esm/modules/word/writer/document-writer.js +473 -0
- package/dist/esm/modules/word/writer/docx-packager.js +1515 -0
- package/dist/{browser/modules/word/writers → esm/modules/word/writer}/footnote-writer.js +13 -10
- package/dist/{browser/modules/word/writers → esm/modules/word/writer}/header-footer-writer.js +39 -21
- package/dist/esm/modules/word/{writers → writer}/image-writer.js +11 -7
- package/dist/{browser/modules/word/writers → esm/modules/word/writer}/math-writer.js +21 -1
- package/dist/{browser/modules/word/writers → esm/modules/word/writer}/numbering-writer.js +11 -4
- package/dist/esm/modules/word/{writers → writer}/paragraph-writer.js +73 -38
- package/dist/{browser/modules/word/writers → esm/modules/word/writer}/parts-writer.js +91 -12
- package/dist/esm/modules/word/writer/reference-scanners.js +111 -0
- package/dist/esm/modules/word/writer/relationships.js +117 -0
- package/dist/esm/modules/word/writer/render-context.js +46 -0
- package/dist/{browser/modules/word/writers → esm/modules/word/writer}/run-writer.js +126 -24
- package/dist/esm/modules/word/writer/sdt-writer.js +189 -0
- package/dist/esm/modules/word/writer/stream-buf.js +73 -0
- package/dist/esm/modules/word/writer/streaming-writer.js +1382 -0
- package/dist/esm/modules/word/writer/string-buf.js +7 -0
- package/dist/esm/modules/word/{writers → writer}/styles-writer.js +32 -1
- package/dist/esm/modules/word/{writers → writer}/table-writer.js +94 -11
- package/dist/esm/utils/crypto.browser.js +3 -1
- package/dist/esm/utils/crypto.js +4 -1
- package/dist/esm/utils/font-metrics.js +293 -0
- package/dist/esm/utils/string-buf.js +89 -0
- package/dist/esm/utils/theme-colors.js +120 -0
- package/dist/iife/excelts.iife.js +70692 -70337
- package/dist/iife/excelts.iife.js.map +1 -1
- package/dist/iife/excelts.iife.min.js +57 -57
- package/dist/types/modules/archive/fs/archive-file.d.ts +8 -5
- package/dist/types/modules/excel/chart/chart-ex-types.d.ts +0 -12
- package/dist/types/modules/excel/chart/chart.d.ts +1 -5
- package/dist/types/modules/excel/chart/types.d.ts +0 -6
- package/dist/types/modules/excel/stream/workbook-writer.browser.d.ts +40 -0
- package/dist/types/modules/excel/utils/string-buf.d.ts +5 -26
- package/dist/types/modules/excel/xlsx/xlsx.browser.d.ts +19 -9
- package/dist/types/modules/excel/xlsx/xlsx.d.ts +10 -2
- package/dist/types/modules/pdf/excel-bridge.d.ts +30 -1
- package/dist/types/modules/pdf/font/metrics.d.ts +3 -52
- package/dist/types/modules/pdf/index.d.ts +1 -1
- package/dist/types/modules/pdf/render-layout-to-pdf.d.ts +66 -0
- package/dist/types/modules/pdf/word-bridge.d.ts +80 -12
- package/dist/types/modules/stream/index.base.d.ts +2 -0
- package/dist/types/modules/stream/internal/sink-adapter.d.ts +65 -0
- package/dist/types/modules/stream/pull-stream.d.ts +19 -2
- package/dist/types/modules/stream/types.d.ts +13 -1
- package/dist/types/modules/word/advanced/diff.d.ts +61 -0
- package/dist/types/modules/word/advanced/drawing-shapes.d.ts +269 -0
- package/dist/types/modules/word/advanced/field-engine.d.ts +43 -0
- package/dist/types/modules/word/advanced/glossary.d.ts +86 -0
- package/dist/types/modules/word/advanced/math-convert.d.ts +30 -0
- package/dist/types/modules/word/advanced/ole-objects.d.ts +115 -0
- package/dist/types/modules/word/advanced/style-map.d.ts +105 -0
- package/dist/types/modules/word/advanced/validation.d.ts +56 -0
- package/dist/types/modules/word/advanced/vba-project.d.ts +91 -0
- package/dist/types/modules/word/bridge/excel-bridge.d.ts +127 -0
- package/dist/types/modules/word/builder/document-handle.d.ts +151 -0
- package/dist/types/modules/word/builder/paragraph-builders.d.ts +61 -0
- package/dist/types/modules/word/builder/run-builders.d.ts +374 -0
- package/dist/types/modules/word/builder/table-builders.d.ts +23 -0
- package/dist/types/modules/word/constants.d.ts +39 -1
- package/dist/types/modules/word/convert/conversion-ir.d.ts +210 -0
- package/dist/types/modules/word/convert/docx-to-semantic.d.ts +39 -0
- package/dist/types/modules/word/convert/flat-opc.d.ts +44 -0
- package/dist/types/modules/word/convert/html/html-import.d.ts +50 -0
- package/dist/{browser/modules/word → types/modules/word/convert/html}/html-renderer.d.ts +14 -1
- package/dist/types/modules/word/convert/html/html.d.ts +15 -0
- package/dist/types/modules/word/convert/markdown/markdown-import.d.ts +68 -0
- package/dist/types/modules/word/convert/markdown/markdown-renderer.d.ts +25 -0
- package/dist/types/modules/word/convert/markdown/markdown.d.ts +15 -0
- package/dist/types/modules/word/convert/odt/odt.d.ts +41 -0
- package/dist/types/modules/word/{color-utils.d.ts → core/color-utils.d.ts} +8 -1
- package/dist/types/modules/word/core/internal-utils.d.ts +90 -0
- package/dist/types/modules/word/core/mapper.d.ts +44 -0
- package/dist/types/modules/word/core/opc-paths.d.ts +33 -0
- package/dist/types/modules/word/core/text-utils.d.ts +38 -0
- package/dist/types/modules/word/core/walker.d.ts +119 -0
- package/dist/types/modules/word/crypto.d.ts +14 -9
- package/dist/types/modules/word/document-io.d.ts +59 -27
- package/dist/types/modules/word/errors.d.ts +44 -1
- package/dist/types/modules/word/excel.d.ts +14 -0
- package/dist/types/modules/word/font/font-embed.d.ts +112 -0
- package/dist/types/modules/word/font/hyphenation.d.ts +65 -0
- package/dist/types/modules/word/font/text-shaping.d.ts +58 -0
- package/dist/types/modules/word/html.d.ts +7 -6
- package/dist/types/modules/word/incremental-edit.d.ts +123 -0
- package/dist/types/modules/word/index.base.d.ts +194 -10
- package/dist/types/modules/word/layout/layout-constants.d.ts +17 -0
- package/dist/types/modules/word/layout/layout-full.d.ts +53 -0
- package/dist/types/modules/word/layout/layout-model.d.ts +344 -0
- package/dist/types/modules/word/layout/layout.d.ts +63 -0
- package/dist/types/modules/word/layout/render-page.d.ts +57 -0
- package/dist/types/modules/word/markdown.d.ts +14 -0
- package/dist/types/modules/word/patcher.d.ts +62 -0
- package/dist/types/modules/word/query/compat.d.ts +25 -0
- package/dist/types/modules/word/query/data-binding.d.ts +22 -0
- package/dist/types/modules/word/query/form-fields.d.ts +41 -0
- package/dist/types/modules/word/query/format-search.d.ts +99 -0
- package/dist/types/modules/word/query/mail-merge.d.ts +25 -0
- package/dist/types/modules/word/query/merge.d.ts +50 -0
- package/dist/types/modules/word/query/replace.d.ts +47 -0
- package/dist/types/modules/word/query/revisions.d.ts +67 -0
- package/dist/types/modules/word/query/search.d.ts +129 -0
- package/dist/types/modules/word/query/split.d.ts +44 -0
- package/dist/types/modules/word/query/style-resolve.d.ts +104 -0
- package/dist/types/modules/word/reader/chart-parser.d.ts +20 -0
- package/dist/types/modules/word/reader/comments-parser.d.ts +26 -0
- package/dist/types/modules/word/reader/doc-props-parsers.d.ts +15 -0
- package/dist/types/modules/word/reader/docx-reader.d.ts +27 -0
- package/dist/types/modules/word/reader/drawing-helpers.d.ts +27 -0
- package/dist/types/modules/word/reader/form-field-parser.d.ts +21 -0
- package/dist/types/modules/word/reader/image-parsers.d.ts +11 -0
- package/dist/types/modules/word/reader/math-parser.d.ts +12 -0
- package/dist/types/modules/word/reader/metadata-parsers.d.ts +17 -0
- package/dist/types/modules/word/reader/numbering-parser.d.ts +13 -0
- package/dist/types/modules/word/reader/paragraph-section-parsers.d.ts +12 -0
- package/dist/types/modules/word/reader/parse-utils.d.ts +91 -0
- package/dist/types/modules/word/reader/properties-parsers.d.ts +21 -0
- package/dist/types/modules/word/reader/reader-context.d.ts +69 -0
- package/dist/types/modules/word/reader/sdt-helpers.d.ts +29 -0
- package/dist/types/modules/word/reader/settings-parser.d.ts +8 -0
- package/dist/types/modules/word/reader/styles-parser.d.ts +12 -0
- package/dist/types/modules/word/reader/table-properties-parsers.d.ts +12 -0
- package/dist/types/modules/word/reader/theme-parser.d.ts +8 -0
- package/dist/types/modules/word/reader/watermark-parser.d.ts +15 -0
- package/dist/types/modules/word/security/cfb-reader.d.ts +37 -0
- package/dist/types/modules/word/{digital-signatures.d.ts → security/digital-signatures.d.ts} +19 -11
- package/dist/types/modules/word/security/document-protection.d.ts +93 -0
- package/dist/{browser/modules/word → types/modules/word/security}/encryption.d.ts +51 -4
- package/dist/types/modules/word/security/policy.d.ts +80 -0
- package/dist/types/modules/word/template/template-chart.d.ts +56 -0
- package/dist/types/modules/word/template/template-datasource.d.ts +154 -0
- package/dist/types/modules/word/template/template-engine.d.ts +121 -0
- package/dist/types/modules/word/types.d.ts +224 -25
- package/dist/types/modules/word/units.d.ts +26 -0
- package/dist/types/modules/word/writer/checkbox-writer.d.ts +17 -0
- package/dist/{browser/modules/word/writers → types/modules/word/writer}/comment-writer.d.ts +2 -1
- package/dist/types/modules/word/writer/common-parts.d.ts +57 -0
- package/dist/{browser/modules/word → types/modules/word/writer}/content-types.d.ts +2 -2
- package/dist/types/modules/word/writer/document-writer.d.ts +24 -0
- package/dist/types/modules/word/writer/docx-packager.d.ts +35 -0
- package/dist/{browser/modules/word/writers → types/modules/word/writer}/footnote-writer.d.ts +3 -2
- package/dist/{browser/modules/word/writers → types/modules/word/writer}/header-footer-writer.d.ts +3 -2
- package/dist/{browser/modules/word/writers → types/modules/word/writer}/image-writer.d.ts +1 -1
- package/dist/types/modules/word/writer/math-writer.d.ts +20 -0
- package/dist/types/modules/word/{writers → writer}/numbering-writer.d.ts +1 -1
- package/dist/types/modules/word/{writers → writer}/paragraph-writer.d.ts +2 -1
- package/dist/types/modules/word/{writers → writer}/parts-writer.d.ts +3 -3
- package/dist/types/modules/word/writer/reference-scanners.d.ts +42 -0
- package/dist/types/modules/word/writer/relationships.d.ts +52 -0
- package/dist/types/modules/word/writer/render-context.d.ts +124 -0
- package/dist/types/modules/word/{writers → writer}/run-writer.d.ts +10 -1
- package/dist/types/modules/word/writer/sdt-writer.d.ts +25 -0
- package/dist/types/modules/word/writer/stream-buf.d.ts +37 -0
- package/dist/types/modules/word/writer/streaming-writer.d.ts +344 -0
- package/dist/types/modules/word/writer/string-buf.d.ts +8 -0
- package/dist/types/modules/word/{writers → writer}/table-writer.d.ts +2 -1
- package/dist/types/modules/xml/types.d.ts +22 -0
- package/dist/types/utils/crypto.browser.d.ts +3 -1
- package/dist/types/utils/crypto.d.ts +4 -1
- package/dist/types/utils/font-metrics.d.ts +63 -0
- package/dist/types/utils/string-buf.d.ts +42 -0
- package/dist/types/utils/theme-colors.d.ts +55 -0
- package/package.json +121 -39
- package/dist/browser/modules/word/color-utils.js +0 -94
- package/dist/browser/modules/word/document.d.ts +0 -657
- package/dist/browser/modules/word/document.js +0 -1533
- package/dist/browser/modules/word/docx-packager.d.ts +0 -14
- package/dist/browser/modules/word/docx-packager.js +0 -822
- package/dist/browser/modules/word/docx-reader.d.ts +0 -11
- package/dist/browser/modules/word/docx-reader.js +0 -4929
- package/dist/browser/modules/word/encryption.js +0 -274
- package/dist/browser/modules/word/internal-utils.d.ts +0 -23
- package/dist/browser/modules/word/internal-utils.js +0 -54
- package/dist/browser/modules/word/namespaces.d.ts +0 -159
- package/dist/browser/modules/word/namespaces.js +0 -189
- package/dist/browser/modules/word/relationships.d.ts +0 -30
- package/dist/browser/modules/word/relationships.js +0 -48
- package/dist/browser/modules/word/writers/checkbox-writer.d.ts +0 -9
- package/dist/browser/modules/word/writers/checkbox-writer.js +0 -42
- package/dist/browser/modules/word/writers/document-writer.d.ts +0 -16
- package/dist/browser/modules/word/writers/document-writer.js +0 -461
- package/dist/browser/modules/word/writers/math-writer.d.ts +0 -9
- package/dist/cjs/modules/word/color-utils.js +0 -97
- package/dist/cjs/modules/word/document.js +0 -1645
- package/dist/cjs/modules/word/docx-packager.js +0 -825
- package/dist/cjs/modules/word/docx-reader.js +0 -4932
- package/dist/cjs/modules/word/encryption.js +0 -282
- package/dist/cjs/modules/word/internal-utils.js +0 -59
- package/dist/cjs/modules/word/namespaces.js +0 -192
- package/dist/cjs/modules/word/relationships.js +0 -55
- package/dist/cjs/modules/word/writers/checkbox-writer.js +0 -45
- package/dist/cjs/modules/word/writers/document-writer.js +0 -465
- package/dist/esm/modules/word/color-utils.js +0 -94
- package/dist/esm/modules/word/document.js +0 -1533
- package/dist/esm/modules/word/docx-packager.js +0 -822
- package/dist/esm/modules/word/docx-reader.js +0 -4929
- package/dist/esm/modules/word/encryption.js +0 -274
- package/dist/esm/modules/word/internal-utils.js +0 -54
- package/dist/esm/modules/word/namespaces.js +0 -189
- package/dist/esm/modules/word/relationships.js +0 -48
- package/dist/esm/modules/word/writers/checkbox-writer.js +0 -42
- package/dist/esm/modules/word/writers/document-writer.js +0 -461
- package/dist/types/modules/word/document.d.ts +0 -657
- package/dist/types/modules/word/docx-packager.d.ts +0 -14
- package/dist/types/modules/word/docx-reader.d.ts +0 -11
- package/dist/types/modules/word/internal-utils.d.ts +0 -23
- package/dist/types/modules/word/namespaces.d.ts +0 -159
- package/dist/types/modules/word/relationships.d.ts +0 -30
- package/dist/types/modules/word/writers/checkbox-writer.d.ts +0 -9
- package/dist/types/modules/word/writers/document-writer.d.ts +0 -16
- package/dist/types/modules/word/writers/math-writer.d.ts +0 -9
- /package/dist/browser/modules/word/{font-obfuscation.d.ts → font/font-obfuscation.d.ts} +0 -0
- /package/dist/browser/modules/word/{writers → writer}/chart-writer.d.ts +0 -0
- /package/dist/browser/modules/word/{writers → writer}/section-writer.d.ts +0 -0
- /package/dist/browser/modules/word/{writers → writer}/section-writer.js +0 -0
- /package/dist/browser/modules/word/{writers → writer}/styles-writer.d.ts +0 -0
- /package/dist/browser/modules/word/{writers → writer}/textbox-writer.d.ts +0 -0
- /package/dist/browser/modules/word/{writers → writer}/textbox-writer.js +0 -0
- /package/dist/browser/modules/word/{writers → writer}/toc-writer.d.ts +0 -0
- /package/dist/browser/modules/word/{writers → writer}/toc-writer.js +0 -0
- /package/dist/cjs/modules/word/{writers → writer}/section-writer.js +0 -0
- /package/dist/cjs/modules/word/{writers → writer}/textbox-writer.js +0 -0
- /package/dist/cjs/modules/word/{writers → writer}/toc-writer.js +0 -0
- /package/dist/esm/modules/word/{writers → writer}/section-writer.js +0 -0
- /package/dist/esm/modules/word/{writers → writer}/textbox-writer.js +0 -0
- /package/dist/esm/modules/word/{writers → writer}/toc-writer.js +0 -0
- /package/dist/types/modules/word/{font-obfuscation.d.ts → font/font-obfuscation.d.ts} +0 -0
- /package/dist/types/modules/word/{writers → writer}/chart-writer.d.ts +0 -0
- /package/dist/types/modules/word/{writers → writer}/section-writer.d.ts +0 -0
- /package/dist/types/modules/word/{writers → writer}/styles-writer.d.ts +0 -0
- /package/dist/types/modules/word/{writers → writer}/textbox-writer.d.ts +0 -0
- /package/dist/types/modules/word/{writers → writer}/toc-writer.d.ts +0 -0
|
@@ -0,0 +1,1382 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DOCX Module - Streaming Writer
|
|
3
|
+
*
|
|
4
|
+
* A DOCX generator that serializes body content incrementally and pushes it
|
|
5
|
+
* through a streaming compression pipeline. Uses the same streaming ZIP
|
|
6
|
+
* infrastructure as the Excel module:
|
|
7
|
+
*
|
|
8
|
+
* - `Zip` (StreamingZip) — streams ZIP entries to output
|
|
9
|
+
* - `ZipDeflate` — per-entry deflate compression
|
|
10
|
+
* - `StreamBuf` — event-driven pipe from XML to ZIP
|
|
11
|
+
* - `StringBuf` — efficient XML string builder with buffer reuse
|
|
12
|
+
*
|
|
13
|
+
* Data flow with sink (true end-to-end streaming):
|
|
14
|
+
* ```
|
|
15
|
+
* add(paragraph) → XML → StreamBuf → ZipDeflate → Zip
|
|
16
|
+
* ↓ (per-chunk callback)
|
|
17
|
+
* await SinkAdapter.write(chunk)
|
|
18
|
+
* ↓
|
|
19
|
+
* user-supplied WritableStream /
|
|
20
|
+
* Node Writable / duck-typed sink
|
|
21
|
+
* ```
|
|
22
|
+
*
|
|
23
|
+
* Memory profile:
|
|
24
|
+
* - Body model is never retained: each element is serialised and
|
|
25
|
+
* compressed as it arrives, so peak per-element memory is
|
|
26
|
+
* O(largest_single_element).
|
|
27
|
+
* - When `options.sink` is provided, compressed bytes are pushed
|
|
28
|
+
* into the sink as soon as they are produced (with backpressure
|
|
29
|
+
* awaited via {@link SinkAdapter}). Total writer-side memory then
|
|
30
|
+
* stays O(largest_part) regardless of final DOCX size.
|
|
31
|
+
* - When `options.sink` is omitted, compressed bytes accumulate in
|
|
32
|
+
* `_outputChunks` and `finalize()` returns the assembled
|
|
33
|
+
* `Uint8Array`. Total memory is O(compressed_docx_size).
|
|
34
|
+
*/
|
|
35
|
+
import { Zip, ZipDeflate } from "../../archive/zip/stream.js";
|
|
36
|
+
import { SinkAdapter } from "../../stream/internal/sink-adapter.js";
|
|
37
|
+
import { xmlEncodeAttr } from "../../xml/encode.js";
|
|
38
|
+
import { XmlWriter } from "../../xml/writer.js";
|
|
39
|
+
import { ContentType, RelType, PartPath, DOCUMENT_NAMESPACES, STD_DOC_ATTRIBUTES } from "../constants.js";
|
|
40
|
+
import { sanitizeMediaFileName, sanitizeUrl, utf8Encoder } from "../core/internal-utils.js";
|
|
41
|
+
import { getFileExt, getPartRelsPath } from "../core/opc-paths.js";
|
|
42
|
+
import { walkBlocks } from "../core/walker.js";
|
|
43
|
+
import { DocxWriteError } from "../errors.js";
|
|
44
|
+
import { renderChartPart } from "./chart-writer.js";
|
|
45
|
+
import { renderComments, renderCommentsExtended } from "./comment-writer.js";
|
|
46
|
+
import { buildCommonAuxiliaryParts } from "./common-parts.js";
|
|
47
|
+
import { createContentTypes, addContentTypeDefault, addContentTypeOverride, addImageContentTypeDefaults, renderContentTypes } from "./content-types.js";
|
|
48
|
+
import { renderBodyContent } from "./document-writer.js";
|
|
49
|
+
import { renderHeader, renderFooter, renderWatermarkHeader } from "./header-footer-writer.js";
|
|
50
|
+
import { collectChartsFromHeaderFooter, collectHyperlinksFromHeaderFooter, collectHyperlinksFromNotes, collectImageRidsFromContent, collectImageRidsFromNotes } from "./reference-scanners.js";
|
|
51
|
+
import { createRelationships, addRelationship, addRelationshipWithId, getRelationshipCount, renderRelationships } from "./relationships.js";
|
|
52
|
+
import { createRenderContext } from "./render-context.js";
|
|
53
|
+
import { renderSectionProperties } from "./section-writer.js";
|
|
54
|
+
import { StreamBuf } from "./stream-buf.js";
|
|
55
|
+
import { StringBuf } from "./string-buf.js";
|
|
56
|
+
// Per-instance StringBuf is created in the constructor (see _xmlBuffer field below).
|
|
57
|
+
// Previously this was a module-level singleton which caused data races with concurrent instances.
|
|
58
|
+
const EMPTY_U8 = new Uint8Array(0);
|
|
59
|
+
// =============================================================================
|
|
60
|
+
// Streaming DOCX Writer
|
|
61
|
+
// =============================================================================
|
|
62
|
+
/**
|
|
63
|
+
* Streaming DOCX writer. Body elements are serialized to XML and compressed
|
|
64
|
+
* into the ZIP pipeline as they arrive, so the body **model** is not retained
|
|
65
|
+
* after each `add()`.
|
|
66
|
+
*
|
|
67
|
+
* When constructed with `options.sink`, compressed bytes are pushed into
|
|
68
|
+
* the sink as soon as the ZIP layer produces them, with backpressure
|
|
69
|
+
* awaited via {@link SinkAdapter}; in this mode peak memory is
|
|
70
|
+
* O(largest_part) and `finalize()` resolves to a zero-length
|
|
71
|
+
* `Uint8Array` (the bytes are already in the sink). Without a sink,
|
|
72
|
+
* compressed bytes accumulate in `_outputChunks` and `finalize()`
|
|
73
|
+
* returns the assembled `Uint8Array` (peak memory
|
|
74
|
+
* O(compressed_docx_size)).
|
|
75
|
+
*
|
|
76
|
+
* Use {@link addAsync} (instead of {@link add}) when driving the sink
|
|
77
|
+
* variant to obtain true end-to-end backpressure: each call awaits all
|
|
78
|
+
* pending sink writes before resolving.
|
|
79
|
+
*/
|
|
80
|
+
export class StreamingDocxWriter {
|
|
81
|
+
constructor(options = {}) {
|
|
82
|
+
this._elementCount = 0;
|
|
83
|
+
this._finalized = false;
|
|
84
|
+
// Per-instance XML buffer (avoids module-level singleton data race)
|
|
85
|
+
this._xmlBuffer = new StringBuf({ size: 65536 });
|
|
86
|
+
/** Compressed-byte accumulator used when no `sink` is supplied. */
|
|
87
|
+
this._outputChunks = [];
|
|
88
|
+
/**
|
|
89
|
+
* Promise chain serialising every sink write. The `Zip` callback fires
|
|
90
|
+
* synchronously, so we queue chunks via `.then(...)` and let
|
|
91
|
+
* `addAsync` / `finalize` await the chain.
|
|
92
|
+
*/
|
|
93
|
+
this._pendingDrain = Promise.resolve();
|
|
94
|
+
this._headerWritten = false;
|
|
95
|
+
/**
|
|
96
|
+
* Whether the previously-written body element was a `<w:tbl>`. Tracked
|
|
97
|
+
* so we can insert a separator `<w:p>` between adjacent tables — Word
|
|
98
|
+
* rejects (and silently merges) two `<w:tbl>` blocks that share no
|
|
99
|
+
* paragraph between them per ECMA-376 §17.13.5.34.
|
|
100
|
+
*/
|
|
101
|
+
this._prevWasTable = false;
|
|
102
|
+
/**
|
|
103
|
+
* First error reported by the underlying ZIP stream (compression failure,
|
|
104
|
+
* write-after-end, etc.). Stored synchronously by the `Zip` callback and
|
|
105
|
+
* surfaced from `finalize()` so callers receive a rejection instead of an
|
|
106
|
+
* indefinitely-pending promise.
|
|
107
|
+
*/
|
|
108
|
+
this._streamError = null;
|
|
109
|
+
/** Charts encountered in body content; rendered to `word/charts/chartN.xml` at finalize time. */
|
|
110
|
+
this._bodyCharts = [];
|
|
111
|
+
/** ChartEx items encountered in body content. */
|
|
112
|
+
this._bodyChartEx = [];
|
|
113
|
+
/**
|
|
114
|
+
* Per-chart sequence numbers fixed at registration time. Both classes
|
|
115
|
+
* use independent monotonic counters; the writer emits
|
|
116
|
+
* `word/charts/chart{n}.xml` for the regular chart family and
|
|
117
|
+
* `word/charts/chartEx{n}.xml` for the chartEx family.
|
|
118
|
+
*
|
|
119
|
+
* The previous scheme used `chartCount + chartExCount + 1` as the
|
|
120
|
+
* sequence number for both classes, which made the rId path encoded in
|
|
121
|
+
* documentRels disagree with the path used at finalize when the writer
|
|
122
|
+
* iterated the two arrays separately. The result was relationships
|
|
123
|
+
* pointing at non-existent chart parts.
|
|
124
|
+
*/
|
|
125
|
+
this._chartNum = new WeakMap();
|
|
126
|
+
this._nextChartSeq = 0;
|
|
127
|
+
this._nextChartExSeq = 0;
|
|
128
|
+
/** Hyperlink object identities already registered (to keep one rId per object). */
|
|
129
|
+
this._registeredHyperlinks = new WeakSet();
|
|
130
|
+
/** Image rIds already registered to documentRels (avoid duplicates). */
|
|
131
|
+
this._registeredImageRIds = new Set();
|
|
132
|
+
/** header map key → newly allocated rId. Populated by `_allocateHeaderFooterRIds`. */
|
|
133
|
+
this._headerKeyToRid = new Map();
|
|
134
|
+
/** footer map key → newly allocated rId. */
|
|
135
|
+
this._footerKeyToRid = new Map();
|
|
136
|
+
// Sanitize image/font file names up-front. They get embedded into
|
|
137
|
+
// ZIP entry paths and into rels Target attributes; a hostile name
|
|
138
|
+
// (e.g. `../../etc/passwd.png` from a round-tripped untrusted DOCX)
|
|
139
|
+
// would otherwise produce a zipslip-shaped output. Mirrors what
|
|
140
|
+
// `packageDocx` does in `shallowCopyDocForPackaging`.
|
|
141
|
+
const sanitized = sanitizeStreamingOptions(options);
|
|
142
|
+
this._options = sanitized;
|
|
143
|
+
this._initZip();
|
|
144
|
+
}
|
|
145
|
+
/** Set a progress callback. */
|
|
146
|
+
onProgress(cb) {
|
|
147
|
+
this._onProgress = cb;
|
|
148
|
+
return this;
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Add a single body element. The element is immediately serialized to XML
|
|
152
|
+
* and pushed into the ZIP compression pipeline. After this call, the element
|
|
153
|
+
* can be garbage collected — it is not retained.
|
|
154
|
+
*/
|
|
155
|
+
add(element) {
|
|
156
|
+
if (this._finalized) {
|
|
157
|
+
throw new DocxWriteError("StreamingDocxWriter: cannot add elements after finalize()");
|
|
158
|
+
}
|
|
159
|
+
// Sink-mode early failure: if a previous chunk already failed to
|
|
160
|
+
// reach the sink, surface that immediately rather than letting the
|
|
161
|
+
// caller keep streaming work that will be discarded. Buffered mode
|
|
162
|
+
// keeps the legacy behaviour of deferring all error reporting to
|
|
163
|
+
// `finalize()` because there is no live consumer that could be
|
|
164
|
+
// disrupted by silent queueing.
|
|
165
|
+
if (this._sinkAdapter && this._streamError) {
|
|
166
|
+
throw new DocxWriteError(`StreamingDocxWriter: sink already failed (${this._streamError.message})`, { cause: this._streamError });
|
|
167
|
+
}
|
|
168
|
+
// Write document.xml header on first element
|
|
169
|
+
if (!this._headerWritten) {
|
|
170
|
+
this._writeDocumentHeader();
|
|
171
|
+
this._headerWritten = true;
|
|
172
|
+
}
|
|
173
|
+
// Register any chart/hyperlink/image references this element introduces
|
|
174
|
+
// BEFORE serializing it, so the per-element renderBodyContent call has a
|
|
175
|
+
// populated WordRenderContext (chart rIds, hyperlink rIds, image remap).
|
|
176
|
+
this._registerElementReferences(element);
|
|
177
|
+
// ECMA-376 §17.13.5.34: a `<w:tbl>` must be followed by a paragraph
|
|
178
|
+
// (or section break) before the next `<w:tbl>` may begin. When the
|
|
179
|
+
// caller streams two adjacent tables we synthesise an empty
|
|
180
|
+
// separator paragraph between them so Word does not collapse them
|
|
181
|
+
// into a single malformed tbl.
|
|
182
|
+
if (element.type === "table" && this._prevWasTable) {
|
|
183
|
+
this._writeSeparatorParagraph();
|
|
184
|
+
}
|
|
185
|
+
// Serialize this single element to XML and push to stream
|
|
186
|
+
this._writeBodyElement(element);
|
|
187
|
+
this._elementCount++;
|
|
188
|
+
this._prevWasTable = element.type === "table";
|
|
189
|
+
if (this._onProgress && this._elementCount % (this._options.chunkSize ?? 1000) === 0) {
|
|
190
|
+
this._onProgress({ elementsWritten: this._elementCount, phase: "body" });
|
|
191
|
+
}
|
|
192
|
+
return this;
|
|
193
|
+
}
|
|
194
|
+
/** Add multiple body elements at once. */
|
|
195
|
+
addMany(elements) {
|
|
196
|
+
for (const el of elements) {
|
|
197
|
+
this.add(el);
|
|
198
|
+
}
|
|
199
|
+
return this;
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* Async variant of {@link add}. After serialising the element, awaits
|
|
203
|
+
* any pending writes to the configured `sink` so callers driving large
|
|
204
|
+
* input get true end-to-end backpressure rather than letting the
|
|
205
|
+
* sink-write queue grow unbounded inside the writer.
|
|
206
|
+
*
|
|
207
|
+
* Without `options.sink` this is equivalent to `add` (resolving
|
|
208
|
+
* synchronously after element serialisation).
|
|
209
|
+
*
|
|
210
|
+
* Throws if the sink reports an error: previous queued writes whose
|
|
211
|
+
* rejection was captured into `_streamError` surface here.
|
|
212
|
+
*/
|
|
213
|
+
async addAsync(element) {
|
|
214
|
+
this.add(element);
|
|
215
|
+
if (this._sinkAdapter) {
|
|
216
|
+
await this._pendingDrain;
|
|
217
|
+
if (this._streamError) {
|
|
218
|
+
throw new DocxWriteError(`StreamingDocxWriter: sink write failed (${this._streamError.message})`, { cause: this._streamError });
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
return this;
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* Async variant of {@link addMany}. Awaits `addAsync` for each element
|
|
225
|
+
* so backpressure is honoured between every body element.
|
|
226
|
+
*/
|
|
227
|
+
async addManyAsync(elements) {
|
|
228
|
+
for (const el of elements) {
|
|
229
|
+
await this.addAsync(el);
|
|
230
|
+
}
|
|
231
|
+
return this;
|
|
232
|
+
}
|
|
233
|
+
/** Add a paragraph with simple text content. */
|
|
234
|
+
addText(content, properties) {
|
|
235
|
+
return this.add({
|
|
236
|
+
type: "paragraph",
|
|
237
|
+
children: [{ content: [{ type: "text", text: content }] }],
|
|
238
|
+
properties
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
/** Get the count of body elements written so far. */
|
|
242
|
+
get elementCount() {
|
|
243
|
+
return this._elementCount;
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* Finalize the document.
|
|
247
|
+
*
|
|
248
|
+
* - Without `options.sink`: returns the assembled `Uint8Array`
|
|
249
|
+
* containing the full DOCX file.
|
|
250
|
+
* - With `options.sink`: drains any pending sink writes, calls
|
|
251
|
+
* `sink.end()`, and resolves to a zero-length `Uint8Array`. The
|
|
252
|
+
* DOCX bytes have already been delivered to the sink — the empty
|
|
253
|
+
* return is a sentinel signalling "writer is done; consumer keeps
|
|
254
|
+
* the data".
|
|
255
|
+
*/
|
|
256
|
+
async finalize() {
|
|
257
|
+
if (this._finalized) {
|
|
258
|
+
throw new DocxWriteError("StreamingDocxWriter: already finalized");
|
|
259
|
+
}
|
|
260
|
+
this._finalized = true;
|
|
261
|
+
if (this._onProgress) {
|
|
262
|
+
this._onProgress({ elementsWritten: this._elementCount, phase: "finalizing" });
|
|
263
|
+
}
|
|
264
|
+
// If no elements were added, still write a minimal document
|
|
265
|
+
if (!this._headerWritten) {
|
|
266
|
+
this._writeDocumentHeader();
|
|
267
|
+
}
|
|
268
|
+
// Allocate header/footer rIds NOW so the section properties we render
|
|
269
|
+
// into document.xml can use the same rIds the auxiliary parts will
|
|
270
|
+
// register later. Without this the section refs and the .rels file
|
|
271
|
+
// would disagree and Word treats the references as dangling.
|
|
272
|
+
this._allocateHeaderFooterRIds();
|
|
273
|
+
// Write document.xml footer (close </w:body></w:document>)
|
|
274
|
+
this._writeDocumentFooter();
|
|
275
|
+
// End the document.xml stream → finalizes its ZIP entry
|
|
276
|
+
await this._endStream(this._documentStream);
|
|
277
|
+
// Add all auxiliary parts (styles, settings, etc.)
|
|
278
|
+
await this._addAuxiliaryParts();
|
|
279
|
+
// Finalize the ZIP archive. Any compression errors during the trailing
|
|
280
|
+
// central-directory write are reported via the `Zip` callback into
|
|
281
|
+
// `_streamError`; surface them as a rejection.
|
|
282
|
+
this._zip.end();
|
|
283
|
+
// In sink mode the Zip callback queued every chunk onto _pendingDrain;
|
|
284
|
+
// wait for that promise chain to complete before declaring the writer
|
|
285
|
+
// finished. In buffered mode this is a no-op (resolved promise).
|
|
286
|
+
await this._pendingDrain;
|
|
287
|
+
if (this._streamError) {
|
|
288
|
+
throw new DocxWriteError(`StreamingDocxWriter: ZIP finalization failed (${this._streamError.message})`, { cause: this._streamError });
|
|
289
|
+
}
|
|
290
|
+
if (this._sinkAdapter) {
|
|
291
|
+
// Close the sink so the consumer knows the byte stream is complete.
|
|
292
|
+
// Errors raised during close (e.g. underlying file system) propagate.
|
|
293
|
+
await this._sinkAdapter.end();
|
|
294
|
+
return EMPTY_U8;
|
|
295
|
+
}
|
|
296
|
+
// Buffered mode: assemble and return the DOCX bytes.
|
|
297
|
+
return this._assembleOutput();
|
|
298
|
+
}
|
|
299
|
+
/** Reset the writer for reuse. */
|
|
300
|
+
/**
|
|
301
|
+
* Reset the writer for reuse.
|
|
302
|
+
*
|
|
303
|
+
* Throws when the writer was constructed with an `options.sink`: a
|
|
304
|
+
* sink can only be `end()`ed once, so reusing the same writer would
|
|
305
|
+
* produce an undefined byte stream. Construct a new writer (with a
|
|
306
|
+
* new sink) for each document instead.
|
|
307
|
+
*/
|
|
308
|
+
reset() {
|
|
309
|
+
if (this._sinkAdapter) {
|
|
310
|
+
throw new DocxWriteError("StreamingDocxWriter: reset() is not supported in sink mode; create a new writer instance per document.");
|
|
311
|
+
}
|
|
312
|
+
this._elementCount = 0;
|
|
313
|
+
this._finalized = false;
|
|
314
|
+
this._headerWritten = false;
|
|
315
|
+
this._prevWasTable = false;
|
|
316
|
+
this._outputChunks = [];
|
|
317
|
+
this._bodyCharts.length = 0;
|
|
318
|
+
this._bodyChartEx.length = 0;
|
|
319
|
+
this._nextChartSeq = 0;
|
|
320
|
+
this._nextChartExSeq = 0;
|
|
321
|
+
this._registeredImageRIds.clear();
|
|
322
|
+
this._headerKeyToRid.clear();
|
|
323
|
+
this._footerKeyToRid.clear();
|
|
324
|
+
this._watermarkHeaderRid = undefined;
|
|
325
|
+
// _registeredHyperlinks is a WeakSet; old entries become unreachable
|
|
326
|
+
// along with the body model objects they referenced — no manual clear
|
|
327
|
+
// is necessary. _chartNum is a WeakMap with the same property.
|
|
328
|
+
this._initZip();
|
|
329
|
+
return this;
|
|
330
|
+
}
|
|
331
|
+
// ===========================================================================
|
|
332
|
+
// Private: ZIP infrastructure
|
|
333
|
+
// ===========================================================================
|
|
334
|
+
_initZip() {
|
|
335
|
+
this._outputChunks = [];
|
|
336
|
+
this._streamError = null;
|
|
337
|
+
this._pendingDrain = Promise.resolve();
|
|
338
|
+
if (this._options.sink && !this._sinkAdapter) {
|
|
339
|
+
this._sinkAdapter = new SinkAdapter(this._options.sink);
|
|
340
|
+
}
|
|
341
|
+
this._documentRels = createRelationships();
|
|
342
|
+
this._renderCtx = createRenderContext({
|
|
343
|
+
securityPolicy: this._options.securityPolicy,
|
|
344
|
+
chartRIds: new Map(),
|
|
345
|
+
imageRIdRemap: new Map(),
|
|
346
|
+
hyperlinkRIds: new WeakMap()
|
|
347
|
+
});
|
|
348
|
+
this._zip = new Zip((err, data, _final) => {
|
|
349
|
+
// The ZIP callback reports compression / framing errors out-of-band.
|
|
350
|
+
// Capture only the first error; subsequent callbacks may still be
|
|
351
|
+
// dispatched as the pipeline drains. Surfaced from `finalize()`.
|
|
352
|
+
if (err && !this._streamError) {
|
|
353
|
+
this._streamError = err;
|
|
354
|
+
// Wake up any pending `_endStream` waiter so callers don't hang.
|
|
355
|
+
this._documentStream?.emit("error", err);
|
|
356
|
+
}
|
|
357
|
+
if (data && data.length > 0) {
|
|
358
|
+
if (this._sinkAdapter) {
|
|
359
|
+
// The Zip callback runs synchronously and cannot await, so we
|
|
360
|
+
// chain each sink write onto a single drain promise. Producers
|
|
361
|
+
// either:
|
|
362
|
+
// (a) call `addAsync()` / `finalize()` which await the chain,
|
|
363
|
+
// (b) ignore backpressure and let chunks queue in memory until
|
|
364
|
+
// the next await point, capped by the sink's own queueing.
|
|
365
|
+
// First-error capture: a rejected write is collapsed into
|
|
366
|
+
// `_streamError` so subsequent writes are skipped quickly.
|
|
367
|
+
this._pendingDrain = this._pendingDrain.then(() => {
|
|
368
|
+
if (this._streamError) {
|
|
369
|
+
return;
|
|
370
|
+
}
|
|
371
|
+
return this._sinkAdapter.write(data).catch((e) => {
|
|
372
|
+
if (!this._streamError) {
|
|
373
|
+
this._streamError = e instanceof Error ? e : new Error(String(e));
|
|
374
|
+
}
|
|
375
|
+
});
|
|
376
|
+
});
|
|
377
|
+
}
|
|
378
|
+
else {
|
|
379
|
+
this._outputChunks.push(data);
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
});
|
|
383
|
+
// Create the document.xml ZIP entry and stream
|
|
384
|
+
const level = this._options.compressionLevel ?? 6;
|
|
385
|
+
this._documentZipFile = new ZipDeflate(PartPath.Document, { level });
|
|
386
|
+
this._zip.add(this._documentZipFile);
|
|
387
|
+
this._documentStream = new StreamBuf({ bufSize: 65536 });
|
|
388
|
+
this._documentStream.on("data", (chunk) => {
|
|
389
|
+
this._documentZipFile.push(chunk);
|
|
390
|
+
});
|
|
391
|
+
this._documentStream.once("finish", () => {
|
|
392
|
+
this._documentZipFile.push(EMPTY_U8, true);
|
|
393
|
+
this._documentStream.emit("zipped");
|
|
394
|
+
});
|
|
395
|
+
}
|
|
396
|
+
_write(text) {
|
|
397
|
+
this._xmlBuffer.reset();
|
|
398
|
+
this._xmlBuffer.addText(text);
|
|
399
|
+
this._documentStream.write(this._xmlBuffer);
|
|
400
|
+
}
|
|
401
|
+
// ===========================================================================
|
|
402
|
+
// Private: Document XML generation
|
|
403
|
+
// ===========================================================================
|
|
404
|
+
_writeDocumentHeader() {
|
|
405
|
+
// XML declaration
|
|
406
|
+
let header = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>`;
|
|
407
|
+
header += `<w:document`;
|
|
408
|
+
for (const [key, value] of Object.entries(DOCUMENT_NAMESPACES)) {
|
|
409
|
+
header += ` ${key}="${value}"`;
|
|
410
|
+
}
|
|
411
|
+
header += `>`;
|
|
412
|
+
// Background
|
|
413
|
+
if (this._options.background) {
|
|
414
|
+
const bg = this._options.background;
|
|
415
|
+
header += `<w:background w:color="${xmlEncodeAttr(bg.color ?? "FFFFFF")}"`;
|
|
416
|
+
if (bg.themeColor) {
|
|
417
|
+
header += ` w:themeColor="${xmlEncodeAttr(bg.themeColor)}"`;
|
|
418
|
+
}
|
|
419
|
+
header += `/>`;
|
|
420
|
+
}
|
|
421
|
+
header += `<w:body>`;
|
|
422
|
+
this._write(header);
|
|
423
|
+
}
|
|
424
|
+
_writeBodyElement(element) {
|
|
425
|
+
// Serialize a single body element using the shared renderBodyContent
|
|
426
|
+
// function. We pass the writer's accumulated render context so r:embed,
|
|
427
|
+
// chart and hyperlink rIds resolve correctly. Without this, charts throw
|
|
428
|
+
// "Chart content was not registered with a relationship id" and hyperlink
|
|
429
|
+
// / image references would be missing or wrong.
|
|
430
|
+
const writer = new XmlWriter();
|
|
431
|
+
renderBodyContent(writer, element, this._renderCtx);
|
|
432
|
+
this._write(writer.xml);
|
|
433
|
+
}
|
|
434
|
+
/**
|
|
435
|
+
* Emit an empty `<w:p/>` to separate two adjacent tables. Required by
|
|
436
|
+
* ECMA-376 §17.13.5.34 — Word rejects packages where two `<w:tbl>`
|
|
437
|
+
* elements appear without a paragraph between them.
|
|
438
|
+
*/
|
|
439
|
+
_writeSeparatorParagraph() {
|
|
440
|
+
this._write("<w:p/>");
|
|
441
|
+
}
|
|
442
|
+
/**
|
|
443
|
+
* Scan a single body element and register any chart / hyperlink / image
|
|
444
|
+
* references it introduces against the writer's accumulated state. This
|
|
445
|
+
* must run before the element is serialized so the render context already
|
|
446
|
+
* carries the relationships the renderer will look up.
|
|
447
|
+
*/
|
|
448
|
+
_registerElementReferences(element) {
|
|
449
|
+
// Direct top-level chart entries
|
|
450
|
+
if (element.type === "chart") {
|
|
451
|
+
this._registerChart(element);
|
|
452
|
+
return;
|
|
453
|
+
}
|
|
454
|
+
if (element.type === "chartEx") {
|
|
455
|
+
this._registerChartEx(element);
|
|
456
|
+
return;
|
|
457
|
+
}
|
|
458
|
+
// For paragraph-like containers we descend with the shared walker so
|
|
459
|
+
// track-change wrappers (InsertedRun / MovedToRun / hyperlink children)
|
|
460
|
+
// are also covered.
|
|
461
|
+
if (element.type === "paragraph" ||
|
|
462
|
+
element.type === "table" ||
|
|
463
|
+
element.type === "sdt" ||
|
|
464
|
+
element.type === "textBox") {
|
|
465
|
+
walkBlocks([element], {
|
|
466
|
+
enterParagraph: para => {
|
|
467
|
+
this._registerParagraphReferences(para);
|
|
468
|
+
},
|
|
469
|
+
enterRun: run => {
|
|
470
|
+
for (const c of run.content) {
|
|
471
|
+
if (c.type === "image" && c.rId) {
|
|
472
|
+
this._registerImageRId(c.rId);
|
|
473
|
+
if (c.svgRId) {
|
|
474
|
+
this._registerImageRId(c.svgRId);
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
},
|
|
479
|
+
enterHyperlink: h => {
|
|
480
|
+
this._registerHyperlink(h);
|
|
481
|
+
}
|
|
482
|
+
});
|
|
483
|
+
return;
|
|
484
|
+
}
|
|
485
|
+
if (element.type === "floatingImage" && element.rId) {
|
|
486
|
+
this._registerImageRId(element.rId);
|
|
487
|
+
if (element.svgRId) {
|
|
488
|
+
this._registerImageRId(element.svgRId);
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
_registerParagraphReferences(_para) {
|
|
493
|
+
// Per-paragraph property registration is not needed today — image and
|
|
494
|
+
// hyperlink registration is handled by enterRun / enterHyperlink. This
|
|
495
|
+
// hook exists so future paragraph-level relationships (numPicBullet,
|
|
496
|
+
// tabLeader image, …) can be added without changing call sites.
|
|
497
|
+
}
|
|
498
|
+
_registerChart(chart) {
|
|
499
|
+
if (this._renderCtx.chartRIds.has(chart)) {
|
|
500
|
+
return;
|
|
501
|
+
}
|
|
502
|
+
const num = ++this._nextChartSeq;
|
|
503
|
+
const rId = addRelationship(this._documentRels, RelType.Chart, `charts/chart${num}.xml`);
|
|
504
|
+
this._renderCtx.chartRIds.set(chart, rId);
|
|
505
|
+
this._chartNum.set(chart, num);
|
|
506
|
+
this._bodyCharts.push(chart);
|
|
507
|
+
}
|
|
508
|
+
_registerChartEx(chart) {
|
|
509
|
+
if (this._renderCtx.chartRIds.has(chart)) {
|
|
510
|
+
return;
|
|
511
|
+
}
|
|
512
|
+
const num = ++this._nextChartExSeq;
|
|
513
|
+
const rId = addRelationship(this._documentRels, RelType.ChartEx, `charts/chartEx${num}.xml`);
|
|
514
|
+
this._renderCtx.chartRIds.set(chart, rId);
|
|
515
|
+
this._chartNum.set(chart, num);
|
|
516
|
+
this._bodyChartEx.push(chart);
|
|
517
|
+
}
|
|
518
|
+
_registerHyperlink(h) {
|
|
519
|
+
if (!h.url || h.rId || this._registeredHyperlinks.has(h)) {
|
|
520
|
+
return;
|
|
521
|
+
}
|
|
522
|
+
// Drop dangerous schemes (javascript:, vbscript:, data:, file:, …) before
|
|
523
|
+
// they reach document.xml.rels. Mark the link as registered either way so
|
|
524
|
+
// we don't keep re-evaluating it on every flush.
|
|
525
|
+
const safe = sanitizeUrl(h.url);
|
|
526
|
+
if (!safe) {
|
|
527
|
+
this._registeredHyperlinks.add(h);
|
|
528
|
+
return;
|
|
529
|
+
}
|
|
530
|
+
const rId = addRelationship(this._documentRels, RelType.Hyperlink, safe, "External");
|
|
531
|
+
this._renderCtx.hyperlinkRIds.set(h, rId);
|
|
532
|
+
this._registeredHyperlinks.add(h);
|
|
533
|
+
}
|
|
534
|
+
_registerImageRId(rId) {
|
|
535
|
+
if (this._registeredImageRIds.has(rId)) {
|
|
536
|
+
return;
|
|
537
|
+
}
|
|
538
|
+
const img = this._lookupImage(rId);
|
|
539
|
+
if (!img) {
|
|
540
|
+
// Image reference points at a binary the caller did not provide. The
|
|
541
|
+
// previous behaviour was to silently skip and emit an invalid DOCX —
|
|
542
|
+
// see the policy field on StreamingDocxOptions.
|
|
543
|
+
const policy = this._options.missingImagePolicy ?? "throw";
|
|
544
|
+
if (policy === "warn") {
|
|
545
|
+
console.warn(`[StreamingDocxWriter] image rId "${rId}" referenced by content ` +
|
|
546
|
+
`but not present in options.images. Output will be missing this ` +
|
|
547
|
+
`relationship and may not open in Word.`);
|
|
548
|
+
return;
|
|
549
|
+
}
|
|
550
|
+
throw new DocxWriteError(`Streaming writer: image rId "${rId}" referenced by content but ` +
|
|
551
|
+
`not present in options.images. Add the image to options.images, ` +
|
|
552
|
+
`remove the reference, or set missingImagePolicy: "warn" to ` +
|
|
553
|
+
`accept a broken document.`);
|
|
554
|
+
}
|
|
555
|
+
addRelationshipWithId(this._documentRels, rId, RelType.Image, `media/${img.fileName}`);
|
|
556
|
+
this._registeredImageRIds.add(rId);
|
|
557
|
+
}
|
|
558
|
+
_lookupImage(rId) {
|
|
559
|
+
if (!this._options.images) {
|
|
560
|
+
return undefined;
|
|
561
|
+
}
|
|
562
|
+
for (const img of this._options.images) {
|
|
563
|
+
if (img.rId === rId) {
|
|
564
|
+
return img;
|
|
565
|
+
}
|
|
566
|
+
// Round-tripped models may surface alias rIds populated by the
|
|
567
|
+
// reader for header/footer-local references that pointed at the
|
|
568
|
+
// same physical media file. Match those too so a header that uses
|
|
569
|
+
// its own rId still resolves to the binary.
|
|
570
|
+
if (img.aliasRIds && img.aliasRIds.includes(rId)) {
|
|
571
|
+
return img;
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
return undefined;
|
|
575
|
+
}
|
|
576
|
+
_writeDocumentFooter() {
|
|
577
|
+
// Word rejects a `<w:body/>` that contains no `<w:p>`. Synthesise an
|
|
578
|
+
// empty paragraph when the caller streamed zero elements so the
|
|
579
|
+
// package opens cleanly. (Bulk packager handles this implicitly via
|
|
580
|
+
// `Document.build`'s default body element list.)
|
|
581
|
+
if (this._elementCount === 0) {
|
|
582
|
+
this._write("<w:p/>");
|
|
583
|
+
}
|
|
584
|
+
// Write final section properties if provided. Header/footer references
|
|
585
|
+
// are rewritten so they refer to the rIds we just allocated in
|
|
586
|
+
// `_allocateHeaderFooterRIds`. References whose target type cannot
|
|
587
|
+
// be resolved (e.g. a custom rId for a header that isn't in the
|
|
588
|
+
// options map) are dropped rather than emitted dangling.
|
|
589
|
+
const sectIn = this._options.sectionProperties;
|
|
590
|
+
let sect = sectIn ? this._rewireSectionRefs(sectIn) : undefined;
|
|
591
|
+
// Auto-fill: if the caller provided headers/footers but no section
|
|
592
|
+
// references, synthesize one per type. Mirrors what the bulk
|
|
593
|
+
// packager does for builder-style usage.
|
|
594
|
+
if (this._options.headers &&
|
|
595
|
+
this._options.headers.size > 0 &&
|
|
596
|
+
(!sect?.headers || sect.headers.length === 0)) {
|
|
597
|
+
const synth = this._synthesizeHeaderRefs();
|
|
598
|
+
if (synth.length > 0) {
|
|
599
|
+
sect = { ...(sect ?? {}), headers: synth };
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
if (this._options.footers &&
|
|
603
|
+
this._options.footers.size > 0 &&
|
|
604
|
+
(!sect?.footers || sect.footers.length === 0)) {
|
|
605
|
+
const synth = this._synthesizeFooterRefs();
|
|
606
|
+
if (synth.length > 0) {
|
|
607
|
+
sect = { ...(sect ?? {}), footers: synth };
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
// Watermark always needs its own header reference, but Word resolves
|
|
611
|
+
// multiple `<w:headerReference w:type="default">` children
|
|
612
|
+
// implementation-defined, so we replace any existing default-type
|
|
613
|
+
// ref instead of stacking them. (User-supplied first/even refs stay.)
|
|
614
|
+
if (this._watermarkHeaderRid) {
|
|
615
|
+
const headers = sect?.headers ? [...sect.headers] : [];
|
|
616
|
+
const filtered = headers.filter(h => h.type !== "default");
|
|
617
|
+
filtered.push({ type: "default", rId: this._watermarkHeaderRid });
|
|
618
|
+
sect = { ...(sect ?? {}), headers: filtered };
|
|
619
|
+
}
|
|
620
|
+
if (sect) {
|
|
621
|
+
const writer = new XmlWriter();
|
|
622
|
+
renderSectionProperties(writer, sect);
|
|
623
|
+
this._write(writer.xml);
|
|
624
|
+
}
|
|
625
|
+
else {
|
|
626
|
+
// OOXML CT_Body requires a final <w:sectPr> so Word knows the page
|
|
627
|
+
// geometry. When the caller didn't provide one, fall back to the
|
|
628
|
+
// same default that Document.build() uses (US Letter, 1" margins).
|
|
629
|
+
const writer = new XmlWriter();
|
|
630
|
+
renderSectionProperties(writer, {
|
|
631
|
+
pageSize: { width: 12240, height: 15840 },
|
|
632
|
+
margins: { top: 1440, right: 1440, bottom: 1440, left: 1440 }
|
|
633
|
+
});
|
|
634
|
+
this._write(writer.xml);
|
|
635
|
+
}
|
|
636
|
+
// Close body and document
|
|
637
|
+
this._write(`</w:body></w:document>`);
|
|
638
|
+
}
|
|
639
|
+
/**
|
|
640
|
+
* Allocate header/footer relationship IDs deterministically (in the same
|
|
641
|
+
* order auxiliary parts will be emitted). Called once during finalize so
|
|
642
|
+
* `_writeDocumentFooter` and `_addAuxiliaryParts` agree on which rId
|
|
643
|
+
* points at which header/footer XML part.
|
|
644
|
+
*/
|
|
645
|
+
_allocateHeaderFooterRIds() {
|
|
646
|
+
if (this._options.headers) {
|
|
647
|
+
let idx = 1;
|
|
648
|
+
for (const [key] of this._options.headers) {
|
|
649
|
+
const rId = addRelationship(this._documentRels, RelType.Header, `header${idx}.xml`);
|
|
650
|
+
this._headerKeyToRid.set(key, rId);
|
|
651
|
+
idx++;
|
|
652
|
+
}
|
|
653
|
+
// Watermark consumes the next header slot.
|
|
654
|
+
if (this._options.watermark) {
|
|
655
|
+
this._watermarkHeaderRid = addRelationship(this._documentRels, RelType.Header, `header${idx}.xml`);
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
else if (this._options.watermark) {
|
|
659
|
+
this._watermarkHeaderRid = addRelationship(this._documentRels, RelType.Header, "header1.xml");
|
|
660
|
+
}
|
|
661
|
+
if (this._options.footers) {
|
|
662
|
+
let idx = 1;
|
|
663
|
+
for (const [key] of this._options.footers) {
|
|
664
|
+
const rId = addRelationship(this._documentRels, RelType.Footer, `footer${idx}.xml`);
|
|
665
|
+
this._footerKeyToRid.set(key, rId);
|
|
666
|
+
idx++;
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
_rewireSectionRefs(sect) {
|
|
671
|
+
const allowedHeader = new Set(this._headerKeyToRid.values());
|
|
672
|
+
const allowedFooter = new Set(this._footerKeyToRid.values());
|
|
673
|
+
const resolveByTypeHeader = (type) => {
|
|
674
|
+
// Try direct map key match first (e.g. user passed map key === rId).
|
|
675
|
+
// Then fall back to a same-type lookup for the common builder case
|
|
676
|
+
// where keys are "default" / "first" / "even".
|
|
677
|
+
if (this._headerKeyToRid.has(type)) {
|
|
678
|
+
return this._headerKeyToRid.get(type);
|
|
679
|
+
}
|
|
680
|
+
return undefined;
|
|
681
|
+
};
|
|
682
|
+
const resolveByTypeFooter = (type) => {
|
|
683
|
+
if (this._footerKeyToRid.has(type)) {
|
|
684
|
+
return this._footerKeyToRid.get(type);
|
|
685
|
+
}
|
|
686
|
+
return undefined;
|
|
687
|
+
};
|
|
688
|
+
let out = sect;
|
|
689
|
+
if (sect.headers) {
|
|
690
|
+
const resolved = [];
|
|
691
|
+
for (const ref of sect.headers) {
|
|
692
|
+
if (ref.rId && allowedHeader.has(ref.rId)) {
|
|
693
|
+
resolved.push(ref);
|
|
694
|
+
continue;
|
|
695
|
+
}
|
|
696
|
+
if (ref.rId && this._headerKeyToRid.has(ref.rId)) {
|
|
697
|
+
resolved.push({ ...ref, rId: this._headerKeyToRid.get(ref.rId) });
|
|
698
|
+
continue;
|
|
699
|
+
}
|
|
700
|
+
const byType = resolveByTypeHeader(ref.type);
|
|
701
|
+
if (byType) {
|
|
702
|
+
resolved.push({ ...ref, rId: byType });
|
|
703
|
+
}
|
|
704
|
+
// else drop — no matching part part, do not emit dangling rId.
|
|
705
|
+
}
|
|
706
|
+
if (resolved.length !== sect.headers.length ||
|
|
707
|
+
resolved.some((r, i) => r !== sect.headers[i])) {
|
|
708
|
+
out = { ...out, headers: resolved };
|
|
709
|
+
}
|
|
710
|
+
}
|
|
711
|
+
if (sect.footers) {
|
|
712
|
+
const resolved = [];
|
|
713
|
+
for (const ref of sect.footers) {
|
|
714
|
+
if (ref.rId && allowedFooter.has(ref.rId)) {
|
|
715
|
+
resolved.push(ref);
|
|
716
|
+
continue;
|
|
717
|
+
}
|
|
718
|
+
if (ref.rId && this._footerKeyToRid.has(ref.rId)) {
|
|
719
|
+
resolved.push({ ...ref, rId: this._footerKeyToRid.get(ref.rId) });
|
|
720
|
+
continue;
|
|
721
|
+
}
|
|
722
|
+
const byType = resolveByTypeFooter(ref.type);
|
|
723
|
+
if (byType) {
|
|
724
|
+
resolved.push({ ...ref, rId: byType });
|
|
725
|
+
}
|
|
726
|
+
}
|
|
727
|
+
if (resolved.length !== sect.footers.length ||
|
|
728
|
+
resolved.some((r, i) => r !== sect.footers[i])) {
|
|
729
|
+
out = { ...out, footers: resolved };
|
|
730
|
+
}
|
|
731
|
+
}
|
|
732
|
+
return out;
|
|
733
|
+
}
|
|
734
|
+
/**
|
|
735
|
+
* Synthesise section-property header references for every header part
|
|
736
|
+
* the caller registered. Recognised type keys (`default`/`first`/`even`)
|
|
737
|
+
* keep their semantics; any other key (round-tripped rId names from
|
|
738
|
+
* readDocx, custom strings) falls back to `"default"` so the header is
|
|
739
|
+
* actually referenced — without this fallback header parts can sit in
|
|
740
|
+
* the package as dangling content.
|
|
741
|
+
*
|
|
742
|
+
* If multiple keys map to the same logical type, only the first one is
|
|
743
|
+
* kept so we don't emit two `<w:headerReference w:type="default">`
|
|
744
|
+
* children (Word's behaviour with duplicates is implementation-defined).
|
|
745
|
+
*/
|
|
746
|
+
_synthesizeHeaderRefs() {
|
|
747
|
+
const out = [];
|
|
748
|
+
const seenTypes = new Set();
|
|
749
|
+
for (const [key, rId] of this._headerKeyToRid) {
|
|
750
|
+
const type = key === "default" || key === "first" || key === "even" ? key : "default";
|
|
751
|
+
if (seenTypes.has(type)) {
|
|
752
|
+
continue;
|
|
753
|
+
}
|
|
754
|
+
seenTypes.add(type);
|
|
755
|
+
out.push({ type, rId });
|
|
756
|
+
}
|
|
757
|
+
return out;
|
|
758
|
+
}
|
|
759
|
+
_synthesizeFooterRefs() {
|
|
760
|
+
const out = [];
|
|
761
|
+
const seenTypes = new Set();
|
|
762
|
+
for (const [key, rId] of this._footerKeyToRid) {
|
|
763
|
+
const type = key === "default" || key === "first" || key === "even" ? key : "default";
|
|
764
|
+
if (seenTypes.has(type)) {
|
|
765
|
+
continue;
|
|
766
|
+
}
|
|
767
|
+
seenTypes.add(type);
|
|
768
|
+
out.push({ type, rId });
|
|
769
|
+
}
|
|
770
|
+
return out;
|
|
771
|
+
}
|
|
772
|
+
// ===========================================================================
|
|
773
|
+
// Private: Auxiliary parts
|
|
774
|
+
// ===========================================================================
|
|
775
|
+
async _addAuxiliaryParts() {
|
|
776
|
+
const level = this._options.compressionLevel ?? 6;
|
|
777
|
+
// Helper: add a complete XML file to the ZIP
|
|
778
|
+
const addXmlFile = (path, renderFn) => {
|
|
779
|
+
const writer = new XmlWriter();
|
|
780
|
+
renderFn(writer);
|
|
781
|
+
const data = utf8Encoder.encode(writer.xml);
|
|
782
|
+
const file = new ZipDeflate(path, { level });
|
|
783
|
+
this._zip.add(file);
|
|
784
|
+
file.push(data, true);
|
|
785
|
+
};
|
|
786
|
+
// Content types and relationships
|
|
787
|
+
const contentTypes = createContentTypes();
|
|
788
|
+
const packageRels = createRelationships();
|
|
789
|
+
// Reuse the document relationships state we have been populating during
|
|
790
|
+
// add() (charts, hyperlinks, images). Adding the standard parts below
|
|
791
|
+
// augments this state.
|
|
792
|
+
const documentRels = this._documentRels;
|
|
793
|
+
// Package relationships
|
|
794
|
+
addRelationship(packageRels, RelType.OfficeDocument, "word/document.xml");
|
|
795
|
+
addRelationship(packageRels, RelType.CoreProperties, "docProps/core.xml");
|
|
796
|
+
addRelationship(packageRels, RelType.ExtendedProperties, "docProps/app.xml");
|
|
797
|
+
// [Content_Types].xml MUST declare every part. The package
|
|
798
|
+
// relationships file references docProps/core.xml + docProps/app.xml
|
|
799
|
+
// even when the caller didn't supply explicit metadata, so we must
|
|
800
|
+
// register their content types up-front; otherwise Word/LibreOffice
|
|
801
|
+
// refuse to open the file (rejected at the OPC layer before any
|
|
802
|
+
// schema validation).
|
|
803
|
+
addContentTypeOverride(contentTypes, `/${PartPath.CoreProps}`, ContentType.CoreProperties);
|
|
804
|
+
addContentTypeOverride(contentTypes, `/${PartPath.AppProps}`, ContentType.ExtendedProperties);
|
|
805
|
+
// Document relationships
|
|
806
|
+
addRelationship(documentRels, RelType.Styles, "styles.xml");
|
|
807
|
+
addRelationship(documentRels, RelType.Settings, "settings.xml");
|
|
808
|
+
addRelationship(documentRels, RelType.FontTable, "fontTable.xml");
|
|
809
|
+
addRelationship(documentRels, RelType.Theme, "theme/theme1.xml");
|
|
810
|
+
// Numbering
|
|
811
|
+
const hasNumbering = (this._options.abstractNumberings && this._options.abstractNumberings.length > 0) ||
|
|
812
|
+
(this._options.numberingInstances && this._options.numberingInstances.length > 0);
|
|
813
|
+
if (hasNumbering) {
|
|
814
|
+
addRelationship(documentRels, RelType.Numbering, "numbering.xml");
|
|
815
|
+
}
|
|
816
|
+
// Footnotes — including their own .rels for in-note hyperlinks/images.
|
|
817
|
+
if (this._options.footnotes && this._options.footnotes.length > 0) {
|
|
818
|
+
addRelationship(documentRels, RelType.Footnotes, "footnotes.xml");
|
|
819
|
+
addContentTypeOverride(contentTypes, `/${PartPath.Footnotes}`, ContentType.Footnotes);
|
|
820
|
+
const fnRels = createRelationships();
|
|
821
|
+
const fnLinks = collectHyperlinksFromNotes(this._options.footnotes);
|
|
822
|
+
for (const link of fnLinks) {
|
|
823
|
+
if (link.url) {
|
|
824
|
+
const safe = sanitizeUrl(link.url);
|
|
825
|
+
if (!safe) {
|
|
826
|
+
continue;
|
|
827
|
+
}
|
|
828
|
+
const linkRId = addRelationship(fnRels, RelType.Hyperlink, safe, "External");
|
|
829
|
+
this._renderCtx.hyperlinkRIds.set(link, linkRId);
|
|
830
|
+
}
|
|
831
|
+
}
|
|
832
|
+
const fnImgs = collectImageRidsFromNotes(this._options.footnotes);
|
|
833
|
+
for (const oldRid of fnImgs) {
|
|
834
|
+
const img = this._lookupImage(oldRid);
|
|
835
|
+
if (img) {
|
|
836
|
+
addRelationshipWithId(fnRels, oldRid, RelType.Image, `media/${img.fileName}`);
|
|
837
|
+
}
|
|
838
|
+
}
|
|
839
|
+
if (getRelationshipCount(fnRels) > 0) {
|
|
840
|
+
addXmlFile(`word/_rels/footnotes.xml.rels`, xml => renderRelationships(fnRels, xml));
|
|
841
|
+
}
|
|
842
|
+
// Footnote XML rendering itself happens in buildCommonAuxiliaryParts.
|
|
843
|
+
}
|
|
844
|
+
// Endnotes — same treatment as footnotes.
|
|
845
|
+
if (this._options.endnotes && this._options.endnotes.length > 0) {
|
|
846
|
+
addRelationship(documentRels, RelType.Endnotes, "endnotes.xml");
|
|
847
|
+
addContentTypeOverride(contentTypes, `/${PartPath.Endnotes}`, ContentType.Endnotes);
|
|
848
|
+
const enRels = createRelationships();
|
|
849
|
+
const enLinks = collectHyperlinksFromNotes(this._options.endnotes);
|
|
850
|
+
for (const link of enLinks) {
|
|
851
|
+
if (link.url) {
|
|
852
|
+
const safe = sanitizeUrl(link.url);
|
|
853
|
+
if (!safe) {
|
|
854
|
+
continue;
|
|
855
|
+
}
|
|
856
|
+
const linkRId = addRelationship(enRels, RelType.Hyperlink, safe, "External");
|
|
857
|
+
this._renderCtx.hyperlinkRIds.set(link, linkRId);
|
|
858
|
+
}
|
|
859
|
+
}
|
|
860
|
+
const enImgs = collectImageRidsFromNotes(this._options.endnotes);
|
|
861
|
+
for (const oldRid of enImgs) {
|
|
862
|
+
const img = this._lookupImage(oldRid);
|
|
863
|
+
if (img) {
|
|
864
|
+
addRelationshipWithId(enRels, oldRid, RelType.Image, `media/${img.fileName}`);
|
|
865
|
+
}
|
|
866
|
+
}
|
|
867
|
+
if (getRelationshipCount(enRels) > 0) {
|
|
868
|
+
addXmlFile(`word/_rels/endnotes.xml.rels`, xml => renderRelationships(enRels, xml));
|
|
869
|
+
}
|
|
870
|
+
}
|
|
871
|
+
// Comments — including their own .rels for in-comment hyperlinks/images.
|
|
872
|
+
if (this._options.comments && this._options.comments.length > 0) {
|
|
873
|
+
addRelationship(documentRels, RelType.Comments, "comments.xml");
|
|
874
|
+
addContentTypeOverride(contentTypes, `/${PartPath.Comments}`, ContentType.Comments);
|
|
875
|
+
// Register hyperlink/image rels BEFORE rendering comments.xml so the
|
|
876
|
+
// emitted r:id values match the per-part .rels we are about to write.
|
|
877
|
+
const cmtRels = createRelationships();
|
|
878
|
+
const commentBodies = this._options.comments.map(c => ({ content: c.content }));
|
|
879
|
+
const cmtLinks = collectHyperlinksFromNotes(commentBodies);
|
|
880
|
+
for (const link of cmtLinks) {
|
|
881
|
+
if (link.url) {
|
|
882
|
+
const safe = sanitizeUrl(link.url);
|
|
883
|
+
if (!safe) {
|
|
884
|
+
continue;
|
|
885
|
+
}
|
|
886
|
+
const linkRId = addRelationship(cmtRels, RelType.Hyperlink, safe, "External");
|
|
887
|
+
this._renderCtx.hyperlinkRIds.set(link, linkRId);
|
|
888
|
+
}
|
|
889
|
+
}
|
|
890
|
+
const cmtImgs = collectImageRidsFromNotes(commentBodies);
|
|
891
|
+
for (const oldRid of cmtImgs) {
|
|
892
|
+
const img = this._lookupImage(oldRid);
|
|
893
|
+
if (img) {
|
|
894
|
+
addRelationshipWithId(cmtRels, oldRid, RelType.Image, `media/${img.fileName}`);
|
|
895
|
+
}
|
|
896
|
+
}
|
|
897
|
+
if (getRelationshipCount(cmtRels) > 0) {
|
|
898
|
+
addXmlFile(`word/_rels/comments.xml.rels`, xml => renderRelationships(cmtRels, xml));
|
|
899
|
+
}
|
|
900
|
+
addXmlFile(PartPath.Comments, xml => renderComments(xml, this._options.comments, {
|
|
901
|
+
imageRemap: this._renderCtx.imageRIdRemap,
|
|
902
|
+
hyperlinkRIds: this._renderCtx.hyperlinkRIds,
|
|
903
|
+
rawXmlPolicy: this._renderCtx.rawXmlPolicy
|
|
904
|
+
}));
|
|
905
|
+
// Also write commentsExtended if any have done/parentId
|
|
906
|
+
const hasExtended = this._options.comments.some(c => c.done != null || c.parentId != null);
|
|
907
|
+
if (hasExtended) {
|
|
908
|
+
addRelationship(documentRels, RelType.CommentsExtended, "commentsExtended.xml");
|
|
909
|
+
addContentTypeOverride(contentTypes, `/${PartPath.CommentsExtended}`, ContentType.CommentsExtended);
|
|
910
|
+
addXmlFile(PartPath.CommentsExtended, xml => renderCommentsExtended(xml, this._options.comments));
|
|
911
|
+
}
|
|
912
|
+
}
|
|
913
|
+
// Headers
|
|
914
|
+
//
|
|
915
|
+
// Each header part has its own .rels file. Image / hyperlink / chart
|
|
916
|
+
// references inside header content must register against THAT part's
|
|
917
|
+
// .rels — they are not document.xml.rels relationships, so we mirror
|
|
918
|
+
// the bulk packager's behaviour here to avoid producing dangling
|
|
919
|
+
// r:embed / r:id values inside header XML.
|
|
920
|
+
//
|
|
921
|
+
// The document-level rId for each header was already allocated during
|
|
922
|
+
// `_allocateHeaderFooterRIds` so that section properties and header
|
|
923
|
+
// parts agree.
|
|
924
|
+
let nextHeaderIdx = 1;
|
|
925
|
+
if (this._options.headers) {
|
|
926
|
+
for (const [, headerDef] of this._options.headers) {
|
|
927
|
+
const headerIdx = nextHeaderIdx++;
|
|
928
|
+
const headerPath = PartPath.header(headerIdx);
|
|
929
|
+
addContentTypeOverride(contentTypes, `/${headerPath}`, ContentType.Header);
|
|
930
|
+
// Register relationships BEFORE rendering the header XML, otherwise
|
|
931
|
+
// the writer cannot resolve the freshly-allocated hyperlink rIds and
|
|
932
|
+
// emits dangling r:id values. addXmlFile() invokes the render
|
|
933
|
+
// callback synchronously, so the order matters.
|
|
934
|
+
const hRels = createRelationships();
|
|
935
|
+
// Images: register every rId referenced inside this header that the
|
|
936
|
+
// caller supplied a binary for. Header XML emits `r:embed` using the
|
|
937
|
+
// model rId, so we register under the same id.
|
|
938
|
+
const imgRids = collectImageRidsFromContent(headerDef.content);
|
|
939
|
+
for (const oldRid of imgRids) {
|
|
940
|
+
const img = this._lookupImage(oldRid);
|
|
941
|
+
if (img) {
|
|
942
|
+
addRelationshipWithId(hRels, oldRid, RelType.Image, `media/${img.fileName}`);
|
|
943
|
+
}
|
|
944
|
+
}
|
|
945
|
+
// Hyperlinks: same scheme as bulk packager — assign a fresh rId per
|
|
946
|
+
// header for any URL-bearing hyperlink and surface it via
|
|
947
|
+
// hyperlinkRIds so the writer emits matching r:id.
|
|
948
|
+
const hLinks = collectHyperlinksFromHeaderFooter(headerDef.content);
|
|
949
|
+
for (const link of hLinks) {
|
|
950
|
+
if (link.url) {
|
|
951
|
+
const safe = sanitizeUrl(link.url);
|
|
952
|
+
if (!safe) {
|
|
953
|
+
continue;
|
|
954
|
+
}
|
|
955
|
+
const linkRId = addRelationship(hRels, RelType.Hyperlink, safe, "External");
|
|
956
|
+
this._renderCtx.hyperlinkRIds.set(link, linkRId);
|
|
957
|
+
}
|
|
958
|
+
}
|
|
959
|
+
// Charts: collect into _bodyCharts so a chart part is generated, and
|
|
960
|
+
// register the rel against the header's own .rels.
|
|
961
|
+
const headerCharts = [];
|
|
962
|
+
collectChartsFromHeaderFooter(headerDef.content, headerCharts);
|
|
963
|
+
for (const chartContent of headerCharts) {
|
|
964
|
+
if (this._renderCtx.chartRIds.has(chartContent)) {
|
|
965
|
+
continue;
|
|
966
|
+
}
|
|
967
|
+
const num = ++this._nextChartSeq;
|
|
968
|
+
const rId = addRelationship(hRels, RelType.Chart, `charts/chart${num}.xml`);
|
|
969
|
+
this._renderCtx.chartRIds.set(chartContent, rId);
|
|
970
|
+
this._chartNum.set(chartContent, num);
|
|
971
|
+
this._bodyCharts.push(chartContent);
|
|
972
|
+
}
|
|
973
|
+
addXmlFile(headerPath, xml => renderHeader(xml, headerDef.content, {
|
|
974
|
+
imageRemap: this._renderCtx.imageRIdRemap,
|
|
975
|
+
hyperlinkRIds: this._renderCtx.hyperlinkRIds,
|
|
976
|
+
rawXmlPolicy: this._renderCtx.rawXmlPolicy
|
|
977
|
+
}));
|
|
978
|
+
if (getRelationshipCount(hRels) > 0) {
|
|
979
|
+
addXmlFile(`word/_rels/header${headerIdx}.xml.rels`, xml => renderRelationships(hRels, xml));
|
|
980
|
+
}
|
|
981
|
+
}
|
|
982
|
+
}
|
|
983
|
+
// Watermark — always rendered as its own header part appended after any
|
|
984
|
+
// user-supplied headers. Its rId was allocated during
|
|
985
|
+
// `_allocateHeaderFooterRIds`.
|
|
986
|
+
if (this._options.watermark) {
|
|
987
|
+
const watermarkIdx = nextHeaderIdx++;
|
|
988
|
+
const watermarkPath = PartPath.header(watermarkIdx);
|
|
989
|
+
addContentTypeOverride(contentTypes, `/${watermarkPath}`, ContentType.Header);
|
|
990
|
+
addXmlFile(watermarkPath, xml => renderWatermarkHeader(xml, this._options.watermark));
|
|
991
|
+
// Image watermarks need a per-header relationship to the image binary.
|
|
992
|
+
if (this._options.watermark.type === "image") {
|
|
993
|
+
const wmRId = this._options.watermark.rId;
|
|
994
|
+
const img = this._lookupImage(wmRId);
|
|
995
|
+
if (img) {
|
|
996
|
+
const wmRels = createRelationships();
|
|
997
|
+
addRelationshipWithId(wmRels, wmRId, RelType.Image, `media/${img.fileName}`);
|
|
998
|
+
addXmlFile(`word/_rels/header${watermarkIdx}.xml.rels`, xml => renderRelationships(wmRels, xml));
|
|
999
|
+
}
|
|
1000
|
+
}
|
|
1001
|
+
}
|
|
1002
|
+
// Footers — document-level rIds already allocated during
|
|
1003
|
+
// `_allocateHeaderFooterRIds`.
|
|
1004
|
+
if (this._options.footers) {
|
|
1005
|
+
let footerIdx = 1;
|
|
1006
|
+
for (const [, footerDef] of this._options.footers) {
|
|
1007
|
+
const footerPath = PartPath.footer(footerIdx);
|
|
1008
|
+
addContentTypeOverride(contentTypes, `/${footerPath}`, ContentType.Footer);
|
|
1009
|
+
// Register relationships BEFORE rendering. addXmlFile() runs the
|
|
1010
|
+
// callback synchronously so any hyperlink rIds the renderer needs
|
|
1011
|
+
// must already be in this._renderCtx.hyperlinkRIds.
|
|
1012
|
+
const fRels = createRelationships();
|
|
1013
|
+
const imgRids = collectImageRidsFromContent(footerDef.content);
|
|
1014
|
+
for (const oldRid of imgRids) {
|
|
1015
|
+
const img = this._lookupImage(oldRid);
|
|
1016
|
+
if (img) {
|
|
1017
|
+
addRelationshipWithId(fRels, oldRid, RelType.Image, `media/${img.fileName}`);
|
|
1018
|
+
}
|
|
1019
|
+
}
|
|
1020
|
+
const fLinks = collectHyperlinksFromHeaderFooter(footerDef.content);
|
|
1021
|
+
for (const link of fLinks) {
|
|
1022
|
+
if (link.url) {
|
|
1023
|
+
const safe = sanitizeUrl(link.url);
|
|
1024
|
+
if (!safe) {
|
|
1025
|
+
continue;
|
|
1026
|
+
}
|
|
1027
|
+
const linkRId = addRelationship(fRels, RelType.Hyperlink, safe, "External");
|
|
1028
|
+
this._renderCtx.hyperlinkRIds.set(link, linkRId);
|
|
1029
|
+
}
|
|
1030
|
+
}
|
|
1031
|
+
const footerCharts = [];
|
|
1032
|
+
collectChartsFromHeaderFooter(footerDef.content, footerCharts);
|
|
1033
|
+
for (const chartContent of footerCharts) {
|
|
1034
|
+
if (this._renderCtx.chartRIds.has(chartContent)) {
|
|
1035
|
+
continue;
|
|
1036
|
+
}
|
|
1037
|
+
const num = ++this._nextChartSeq;
|
|
1038
|
+
const rId = addRelationship(fRels, RelType.Chart, `charts/chart${num}.xml`);
|
|
1039
|
+
this._renderCtx.chartRIds.set(chartContent, rId);
|
|
1040
|
+
this._chartNum.set(chartContent, num);
|
|
1041
|
+
this._bodyCharts.push(chartContent);
|
|
1042
|
+
}
|
|
1043
|
+
addXmlFile(footerPath, xml => renderFooter(xml, footerDef.content, {
|
|
1044
|
+
imageRemap: this._renderCtx.imageRIdRemap,
|
|
1045
|
+
hyperlinkRIds: this._renderCtx.hyperlinkRIds,
|
|
1046
|
+
rawXmlPolicy: this._renderCtx.rawXmlPolicy
|
|
1047
|
+
}));
|
|
1048
|
+
if (getRelationshipCount(fRels) > 0) {
|
|
1049
|
+
addXmlFile(`word/_rels/footer${footerIdx}.xml.rels`, xml => renderRelationships(fRels, xml));
|
|
1050
|
+
}
|
|
1051
|
+
footerIdx++;
|
|
1052
|
+
}
|
|
1053
|
+
}
|
|
1054
|
+
// Custom properties
|
|
1055
|
+
if (this._options.customProperties && this._options.customProperties.length > 0) {
|
|
1056
|
+
addRelationship(packageRels, RelType.CustomProperties, "docProps/custom.xml");
|
|
1057
|
+
addContentTypeOverride(contentTypes, `/${PartPath.CustomProps}`, ContentType.CustomProperties);
|
|
1058
|
+
// XML rendering handled by buildCommonAuxiliaryParts below
|
|
1059
|
+
}
|
|
1060
|
+
// Images. Only register images here that were not already registered
|
|
1061
|
+
// by `_registerImageRId` during add(). For round-tripped models, the
|
|
1062
|
+
// image's `aliasRIds` may have been registered earlier (when a
|
|
1063
|
+
// header/footer body referenced the image under one of those alias
|
|
1064
|
+
// names) — treat any of those aliases as "already registered" so we
|
|
1065
|
+
// don't duplicate the relationship under the canonical rId. Images
|
|
1066
|
+
// supplied via options but never referenced in body content also get
|
|
1067
|
+
// registered (since the user clearly intended them to be part of the
|
|
1068
|
+
// document) but with an anonymous rId.
|
|
1069
|
+
if (this._options.images) {
|
|
1070
|
+
const extensions = new Set();
|
|
1071
|
+
for (const img of this._options.images) {
|
|
1072
|
+
const ext = getFileExt(img.fileName);
|
|
1073
|
+
if (ext) {
|
|
1074
|
+
extensions.add(ext);
|
|
1075
|
+
}
|
|
1076
|
+
const alreadyRegistered = (img.rId && this._registeredImageRIds.has(img.rId)) ||
|
|
1077
|
+
(img.aliasRIds && img.aliasRIds.some(a => this._registeredImageRIds.has(a)));
|
|
1078
|
+
if (alreadyRegistered) {
|
|
1079
|
+
continue;
|
|
1080
|
+
}
|
|
1081
|
+
if (img.rId) {
|
|
1082
|
+
addRelationshipWithId(documentRels, img.rId, RelType.Image, `media/${img.fileName}`);
|
|
1083
|
+
this._registeredImageRIds.add(img.rId);
|
|
1084
|
+
}
|
|
1085
|
+
else {
|
|
1086
|
+
addRelationship(documentRels, RelType.Image, `media/${img.fileName}`);
|
|
1087
|
+
}
|
|
1088
|
+
}
|
|
1089
|
+
addImageContentTypeDefaults(contentTypes, extensions);
|
|
1090
|
+
}
|
|
1091
|
+
// Custom XML parts (for SDT data binding)
|
|
1092
|
+
if (this._options.customXmlParts && this._options.customXmlParts.length > 0) {
|
|
1093
|
+
this._options.customXmlParts.forEach((part, i) => {
|
|
1094
|
+
const num = i + 1;
|
|
1095
|
+
const itemPath = `word/customXml/item${num}.xml`;
|
|
1096
|
+
const propsPath = `word/customXml/itemProps${num}.xml`;
|
|
1097
|
+
// Write the XML content
|
|
1098
|
+
const itemData = utf8Encoder.encode(part.xmlContent);
|
|
1099
|
+
const itemFile = new ZipDeflate(itemPath, { level });
|
|
1100
|
+
this._zip.add(itemFile);
|
|
1101
|
+
itemFile.push(itemData, true);
|
|
1102
|
+
// Write itemProps*.xml
|
|
1103
|
+
const propsWriter = new XmlWriter();
|
|
1104
|
+
propsWriter.openXml(STD_DOC_ATTRIBUTES);
|
|
1105
|
+
propsWriter.openNode("ds:datastoreItem", {
|
|
1106
|
+
"ds:itemID": `{${part.itemId}}`,
|
|
1107
|
+
"xmlns:ds": "http://schemas.openxmlformats.org/officeDocument/2006/customXml"
|
|
1108
|
+
});
|
|
1109
|
+
if (part.schemaReferences && part.schemaReferences.length > 0) {
|
|
1110
|
+
propsWriter.openNode("ds:schemaRefs");
|
|
1111
|
+
for (const uri of part.schemaReferences) {
|
|
1112
|
+
propsWriter.leafNode("ds:schemaRef", { "ds:uri": uri });
|
|
1113
|
+
}
|
|
1114
|
+
propsWriter.closeNode();
|
|
1115
|
+
}
|
|
1116
|
+
else {
|
|
1117
|
+
propsWriter.leafNode("ds:schemaRefs");
|
|
1118
|
+
}
|
|
1119
|
+
propsWriter.closeNode();
|
|
1120
|
+
const propsData = utf8Encoder.encode(propsWriter.xml);
|
|
1121
|
+
const propsFile = new ZipDeflate(propsPath, { level });
|
|
1122
|
+
this._zip.add(propsFile);
|
|
1123
|
+
propsFile.push(propsData, true);
|
|
1124
|
+
// Write item rels (links itemN.xml → itemPropsN.xml)
|
|
1125
|
+
const itemRels = createRelationships();
|
|
1126
|
+
addRelationship(itemRels, RelType.CustomXmlProps, `itemProps${num}.xml`);
|
|
1127
|
+
addXmlFile(`word/customXml/_rels/item${num}.xml.rels`, xml => renderRelationships(itemRels, xml));
|
|
1128
|
+
// Register content types
|
|
1129
|
+
addContentTypeOverride(contentTypes, `/word/customXml/itemProps${num}.xml`, "application/vnd.openxmlformats-officedocument.customXmlProperties+xml");
|
|
1130
|
+
// Add to document rels
|
|
1131
|
+
addRelationship(documentRels, RelType.CustomXml, `customXml/item${num}.xml`);
|
|
1132
|
+
});
|
|
1133
|
+
}
|
|
1134
|
+
// Embedded fonts
|
|
1135
|
+
if (this._options.embeddedFonts && this._options.embeddedFonts.length > 0) {
|
|
1136
|
+
const fontTableRels = createRelationships();
|
|
1137
|
+
for (const ef of this._options.embeddedFonts) {
|
|
1138
|
+
const partPath = `word/fonts/${ef.fileName}`;
|
|
1139
|
+
const fontFile = new ZipDeflate(partPath, { level: 0 });
|
|
1140
|
+
this._zip.add(fontFile);
|
|
1141
|
+
fontFile.push(ef.data, true);
|
|
1142
|
+
// Register relationship from fontTable.xml
|
|
1143
|
+
addRelationshipWithId(fontTableRels, ef.rId, RelType.Font, `fonts/${ef.fileName}`);
|
|
1144
|
+
// Register content type for .odttf / .ttf / .otf
|
|
1145
|
+
const ext = getFileExt(ef.fileName);
|
|
1146
|
+
if (ext === "odttf") {
|
|
1147
|
+
addContentTypeDefault(contentTypes, "odttf", ContentType.ObfuscatedFont);
|
|
1148
|
+
}
|
|
1149
|
+
else if (ext === "ttf") {
|
|
1150
|
+
addContentTypeDefault(contentTypes, "ttf", "application/x-font-ttf");
|
|
1151
|
+
}
|
|
1152
|
+
else if (ext === "otf") {
|
|
1153
|
+
addContentTypeDefault(contentTypes, "otf", "application/x-font-otf");
|
|
1154
|
+
}
|
|
1155
|
+
}
|
|
1156
|
+
// Write fontTable.xml.rels
|
|
1157
|
+
addXmlFile("word/_rels/fontTable.xml.rels", xml => renderRelationships(fontTableRels, xml));
|
|
1158
|
+
}
|
|
1159
|
+
// Opaque (unrecognized) parts for round-trip preservation
|
|
1160
|
+
if (this._options.opaqueParts) {
|
|
1161
|
+
const preserveOle = this._options.securityPolicy?.preserveOleObjects !== false;
|
|
1162
|
+
const dropSignatures = this._options.securityPolicy?.dropSignaturesOnModify !== false;
|
|
1163
|
+
for (const part of this._options.opaqueParts) {
|
|
1164
|
+
// Honour `preserveOleObjects`: skip OLE binaries when disabled.
|
|
1165
|
+
if (!preserveOle &&
|
|
1166
|
+
(part.path.startsWith("word/embeddings/") ||
|
|
1167
|
+
(part.path.endsWith(".bin") && part.path.includes("embed")))) {
|
|
1168
|
+
continue;
|
|
1169
|
+
}
|
|
1170
|
+
// Honour `dropSignaturesOnModify`: signatures cease to be valid the
|
|
1171
|
+
// moment the document is re-serialised, so by default we drop them.
|
|
1172
|
+
if (dropSignatures && part.path.startsWith("_xmlsignatures/")) {
|
|
1173
|
+
continue;
|
|
1174
|
+
}
|
|
1175
|
+
const opaqueFile = new ZipDeflate(part.path, { level });
|
|
1176
|
+
this._zip.add(opaqueFile);
|
|
1177
|
+
opaqueFile.push(part.data, true);
|
|
1178
|
+
// Register content type
|
|
1179
|
+
if (part.contentType) {
|
|
1180
|
+
addContentTypeOverride(contentTypes, `/${part.path}`, part.contentType);
|
|
1181
|
+
}
|
|
1182
|
+
// Write part relationships if any
|
|
1183
|
+
if (part.relationships && part.relationships.length > 0) {
|
|
1184
|
+
const partRels = createRelationships();
|
|
1185
|
+
for (const rel of part.relationships) {
|
|
1186
|
+
addRelationshipWithId(partRels, rel.id, rel.type, rel.target, rel.targetMode);
|
|
1187
|
+
}
|
|
1188
|
+
const relsPath = getPartRelsPath(part.path);
|
|
1189
|
+
addXmlFile(relsPath, xml => renderRelationships(partRels, xml));
|
|
1190
|
+
}
|
|
1191
|
+
}
|
|
1192
|
+
}
|
|
1193
|
+
// Content type overrides
|
|
1194
|
+
addContentTypeOverride(contentTypes, `/${PartPath.Document}`, ContentType.Document);
|
|
1195
|
+
addContentTypeOverride(contentTypes, `/${PartPath.Styles}`, ContentType.Styles);
|
|
1196
|
+
addContentTypeOverride(contentTypes, `/${PartPath.Settings}`, ContentType.Settings);
|
|
1197
|
+
addContentTypeOverride(contentTypes, `/${PartPath.FontTable}`, ContentType.FontTable);
|
|
1198
|
+
addContentTypeOverride(contentTypes, `/${PartPath.Theme}`, ContentType.Theme);
|
|
1199
|
+
if (hasNumbering) {
|
|
1200
|
+
addContentTypeOverride(contentTypes, `/${PartPath.Numbering}`, ContentType.Numbering);
|
|
1201
|
+
}
|
|
1202
|
+
// Write common auxiliary parts (styles, settings, fontTable, theme, numbering, properties)
|
|
1203
|
+
// using the shared builder to avoid duplicating render logic with docx-packager.
|
|
1204
|
+
const commonParts = buildCommonAuxiliaryParts({
|
|
1205
|
+
docDefaults: this._options.docDefaults,
|
|
1206
|
+
styles: this._options.styles,
|
|
1207
|
+
settings: this._options.settings,
|
|
1208
|
+
fonts: this._options.fonts,
|
|
1209
|
+
theme: this._options.theme,
|
|
1210
|
+
abstractNumberings: this._options.abstractNumberings,
|
|
1211
|
+
numberingInstances: this._options.numberingInstances,
|
|
1212
|
+
footnotes: this._options.footnotes,
|
|
1213
|
+
endnotes: this._options.endnotes,
|
|
1214
|
+
coreProperties: this._options.coreProperties,
|
|
1215
|
+
appProperties: this._options.appProperties,
|
|
1216
|
+
customProperties: this._options.customProperties,
|
|
1217
|
+
rawXmlPolicy: this._renderCtx.rawXmlPolicy,
|
|
1218
|
+
// Pass the shared rId tables so in-note hyperlinks (registered earlier
|
|
1219
|
+
// against footnotes.xml.rels / endnotes.xml.rels) resolve to the same
|
|
1220
|
+
// rIds the renderer is about to emit.
|
|
1221
|
+
notesHelpers: {
|
|
1222
|
+
imageRemap: this._renderCtx.imageRIdRemap,
|
|
1223
|
+
hyperlinkRIds: this._renderCtx.hyperlinkRIds,
|
|
1224
|
+
rawXmlPolicy: this._renderCtx.rawXmlPolicy
|
|
1225
|
+
}
|
|
1226
|
+
});
|
|
1227
|
+
for (const part of commonParts) {
|
|
1228
|
+
const data = utf8Encoder.encode(part.content);
|
|
1229
|
+
const file = new ZipDeflate(part.path, { level });
|
|
1230
|
+
this._zip.add(file);
|
|
1231
|
+
file.push(data, true);
|
|
1232
|
+
}
|
|
1233
|
+
// Write images
|
|
1234
|
+
if (this._options.images) {
|
|
1235
|
+
for (const img of this._options.images) {
|
|
1236
|
+
const file = new ZipDeflate(PartPath.media(img.fileName), { level: 0 });
|
|
1237
|
+
this._zip.add(file);
|
|
1238
|
+
file.push(img.data, true);
|
|
1239
|
+
}
|
|
1240
|
+
}
|
|
1241
|
+
// Write chart parts (chartN.xml + content type) for body charts
|
|
1242
|
+
// registered during add(). Use the per-chart sequence number captured
|
|
1243
|
+
// at registration time so the rId target encoded in documentRels and
|
|
1244
|
+
// the actual ZIP entry name agree.
|
|
1245
|
+
for (const chartContent of this._bodyCharts) {
|
|
1246
|
+
const num = this._chartNum.get(chartContent);
|
|
1247
|
+
if (num === undefined) {
|
|
1248
|
+
continue; // shouldn't happen; defensive
|
|
1249
|
+
}
|
|
1250
|
+
const chartPath = `word/charts/chart${num}.xml`;
|
|
1251
|
+
const w = new XmlWriter();
|
|
1252
|
+
renderChartPart(w, chartContent.chart);
|
|
1253
|
+
const data = utf8Encoder.encode(w.xml);
|
|
1254
|
+
const file = new ZipDeflate(chartPath, { level });
|
|
1255
|
+
this._zip.add(file);
|
|
1256
|
+
file.push(data, true);
|
|
1257
|
+
addContentTypeOverride(contentTypes, `/${chartPath}`, ContentType.Chart);
|
|
1258
|
+
}
|
|
1259
|
+
// Write ChartEx parts (raw cx:chartSpace XML preserved on the model)
|
|
1260
|
+
for (const cxContent of this._bodyChartEx) {
|
|
1261
|
+
const num = this._chartNum.get(cxContent);
|
|
1262
|
+
if (num === undefined) {
|
|
1263
|
+
continue;
|
|
1264
|
+
}
|
|
1265
|
+
const cxPath = `word/charts/chartEx${num}.xml`;
|
|
1266
|
+
const data = utf8Encoder.encode(cxContent.chartExXml);
|
|
1267
|
+
const file = new ZipDeflate(cxPath, { level });
|
|
1268
|
+
this._zip.add(file);
|
|
1269
|
+
file.push(data, true);
|
|
1270
|
+
addContentTypeOverride(contentTypes, `/${cxPath}`, ContentType.ChartEx);
|
|
1271
|
+
}
|
|
1272
|
+
// Write document.xml.rels
|
|
1273
|
+
addXmlFile(PartPath.DocumentRels, xml => renderRelationships(documentRels, xml));
|
|
1274
|
+
// Write _rels/.rels
|
|
1275
|
+
addXmlFile(PartPath.PackageRels, xml => renderRelationships(packageRels, xml));
|
|
1276
|
+
// Write [Content_Types].xml
|
|
1277
|
+
addXmlFile(PartPath.ContentTypes, xml => renderContentTypes(contentTypes, xml));
|
|
1278
|
+
}
|
|
1279
|
+
_endStream(stream) {
|
|
1280
|
+
return new Promise((resolve, reject) => {
|
|
1281
|
+
// If a prior callback already reported an error, surface it
|
|
1282
|
+
// synchronously instead of waiting for an event that won't fire.
|
|
1283
|
+
if (this._streamError) {
|
|
1284
|
+
reject(this._streamError);
|
|
1285
|
+
return;
|
|
1286
|
+
}
|
|
1287
|
+
stream.once("zipped", () => resolve());
|
|
1288
|
+
stream.once("error", (err) => reject(err));
|
|
1289
|
+
stream.end();
|
|
1290
|
+
});
|
|
1291
|
+
}
|
|
1292
|
+
_assembleOutput() {
|
|
1293
|
+
if (this._outputChunks.length === 1) {
|
|
1294
|
+
return this._outputChunks[0];
|
|
1295
|
+
}
|
|
1296
|
+
const total = this._outputChunks.reduce((sum, c) => sum + c.length, 0);
|
|
1297
|
+
const result = new Uint8Array(total);
|
|
1298
|
+
let offset = 0;
|
|
1299
|
+
for (const chunk of this._outputChunks) {
|
|
1300
|
+
result.set(chunk, offset);
|
|
1301
|
+
offset += chunk.length;
|
|
1302
|
+
}
|
|
1303
|
+
return result;
|
|
1304
|
+
}
|
|
1305
|
+
}
|
|
1306
|
+
/**
|
|
1307
|
+
* Create a new streaming DOCX writer.
|
|
1308
|
+
*
|
|
1309
|
+
* @example
|
|
1310
|
+
* ```ts
|
|
1311
|
+
* const writer = createDocxStream({
|
|
1312
|
+
* styles: [{ type: "paragraph", styleId: "Normal", name: "Normal" }]
|
|
1313
|
+
* });
|
|
1314
|
+
*
|
|
1315
|
+
* for (let i = 0; i < 100000; i++) {
|
|
1316
|
+
* writer.addText(`Paragraph ${i}`);
|
|
1317
|
+
* }
|
|
1318
|
+
*
|
|
1319
|
+
* const buffer = await writer.finalize();
|
|
1320
|
+
* ```
|
|
1321
|
+
*/
|
|
1322
|
+
export function createDocxStream(options) {
|
|
1323
|
+
return new StreamingDocxWriter(options);
|
|
1324
|
+
}
|
|
1325
|
+
/**
|
|
1326
|
+
* Replace any image/font file names in `options` with a leaf form that's
|
|
1327
|
+
* safe to embed into a ZIP entry path. A new options object is returned;
|
|
1328
|
+
* the caller's input is not mutated.
|
|
1329
|
+
*
|
|
1330
|
+
* Names are deduplicated within their respective collection so two
|
|
1331
|
+
* different inputs that sanitise to the same string don't silently
|
|
1332
|
+
* overwrite each other in the output package.
|
|
1333
|
+
*/
|
|
1334
|
+
function sanitizeStreamingOptions(options) {
|
|
1335
|
+
let next = options;
|
|
1336
|
+
if (options.images && options.images.length > 0) {
|
|
1337
|
+
const used = new Set();
|
|
1338
|
+
let mutated = false;
|
|
1339
|
+
const images = options.images.map(img => {
|
|
1340
|
+
const safe = uniqueSanitizedName(img.fileName, used, "image.bin");
|
|
1341
|
+
if (safe !== img.fileName) {
|
|
1342
|
+
mutated = true;
|
|
1343
|
+
return { ...img, fileName: safe };
|
|
1344
|
+
}
|
|
1345
|
+
return img;
|
|
1346
|
+
});
|
|
1347
|
+
if (mutated) {
|
|
1348
|
+
next = { ...next, images };
|
|
1349
|
+
}
|
|
1350
|
+
}
|
|
1351
|
+
if (options.embeddedFonts && options.embeddedFonts.length > 0) {
|
|
1352
|
+
const used = new Set();
|
|
1353
|
+
let mutated = false;
|
|
1354
|
+
const embeddedFonts = options.embeddedFonts.map(ef => {
|
|
1355
|
+
const safe = uniqueSanitizedName(ef.fileName, used, "font.bin");
|
|
1356
|
+
if (safe !== ef.fileName) {
|
|
1357
|
+
mutated = true;
|
|
1358
|
+
return { ...ef, fileName: safe };
|
|
1359
|
+
}
|
|
1360
|
+
return ef;
|
|
1361
|
+
});
|
|
1362
|
+
if (mutated) {
|
|
1363
|
+
next = { ...next, embeddedFonts };
|
|
1364
|
+
}
|
|
1365
|
+
}
|
|
1366
|
+
return next;
|
|
1367
|
+
}
|
|
1368
|
+
function uniqueSanitizedName(raw, used, fallback) {
|
|
1369
|
+
let candidate = sanitizeMediaFileName(raw, fallback);
|
|
1370
|
+
if (used.has(candidate)) {
|
|
1371
|
+
const dot = candidate.lastIndexOf(".");
|
|
1372
|
+
const stem = dot >= 0 ? candidate.slice(0, dot) : candidate;
|
|
1373
|
+
const ext = dot >= 0 ? candidate.slice(dot) : "";
|
|
1374
|
+
let n = 2;
|
|
1375
|
+
while (used.has(`${stem}_${n}${ext}`)) {
|
|
1376
|
+
n++;
|
|
1377
|
+
}
|
|
1378
|
+
candidate = `${stem}_${n}${ext}`;
|
|
1379
|
+
}
|
|
1380
|
+
used.add(candidate);
|
|
1381
|
+
return candidate;
|
|
1382
|
+
}
|