@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,1515 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DOCX Module - Packager
|
|
3
|
+
*
|
|
4
|
+
* Assembles a DocxDocument model into a DOCX ZIP package using the
|
|
5
|
+
* archive and XML modules. Supports all parts including comments,
|
|
6
|
+
* custom properties, document background, hyperlink relationships,
|
|
7
|
+
* and per-header/footer relationship files.
|
|
8
|
+
*/
|
|
9
|
+
import { zip } from "../../archive/create-archive.js";
|
|
10
|
+
import { XmlWriter } from "../../xml/writer.js";
|
|
11
|
+
import { ContentType, RelType, PartPath, STD_DOC_ATTRIBUTES } from "../constants.js";
|
|
12
|
+
import { sanitizeMediaFileName, sanitizeUrl } from "../core/internal-utils.js";
|
|
13
|
+
import { getFileExt, getPartRelsPath } from "../core/opc-paths.js";
|
|
14
|
+
import { isRun } from "../core/text-utils.js";
|
|
15
|
+
import { walkBlocks } from "../core/walker.js";
|
|
16
|
+
import { DocxWriteError } from "../errors.js";
|
|
17
|
+
import { resolveSecurityPolicy } from "../security/policy.js";
|
|
18
|
+
import { renderChartPart } from "./chart-writer.js";
|
|
19
|
+
import { renderComments, renderCommentsExtended } from "./comment-writer.js";
|
|
20
|
+
import { createContentTypes, addContentTypeDefault, addContentTypeOverride, addImageContentTypeDefaults, renderContentTypes } from "./content-types.js";
|
|
21
|
+
import { renderDocument } from "./document-writer.js";
|
|
22
|
+
import { renderFootnotes, renderEndnotes } from "./footnote-writer.js";
|
|
23
|
+
import { renderHeader, renderFooter, renderWatermarkHeader } from "./header-footer-writer.js";
|
|
24
|
+
import { renderNumbering } from "./numbering-writer.js";
|
|
25
|
+
import { renderSettings, renderFontTable, renderCoreProperties, renderAppProperties, renderCustomProperties, renderWebSettings, renderPeople, renderTheme } from "./parts-writer.js";
|
|
26
|
+
import { collectChartsFromHeaderFooter, collectHyperlinksFromHeaderFooter, collectImageRidsFromContent, scanChildrenForHyperlinks, scanChildrenForImages } from "./reference-scanners.js";
|
|
27
|
+
import { createRelationships, addRelationship, addRelationshipWithId, getRelationshipCount, renderRelationships } from "./relationships.js";
|
|
28
|
+
import { createIdGenerators, createRenderContext } from "./render-context.js";
|
|
29
|
+
import { renderStyles } from "./styles-writer.js";
|
|
30
|
+
/** Render XML to string using XmlWriter. */
|
|
31
|
+
function renderXml(renderFn) {
|
|
32
|
+
const writer = new XmlWriter();
|
|
33
|
+
renderFn(writer);
|
|
34
|
+
return writer.xml;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Walk the document and return the highest `id` used on any
|
|
38
|
+
* `StructuredDocumentTag`. Used to seed the SDT id generator so that
|
|
39
|
+
* auto-assigned ids start strictly above any author-supplied ones — a
|
|
40
|
+
* collision would silently break repeating-section / data-binding linkage
|
|
41
|
+
* because Word matches SDTs by id.
|
|
42
|
+
*/
|
|
43
|
+
function scanMaxSdtId(doc) {
|
|
44
|
+
let max = 0;
|
|
45
|
+
const visit = (blocks) => {
|
|
46
|
+
for (const block of blocks) {
|
|
47
|
+
if (block.type === "sdt") {
|
|
48
|
+
const id = block.properties?.id;
|
|
49
|
+
if (typeof id === "number" && Number.isFinite(id) && id > max) {
|
|
50
|
+
max = id;
|
|
51
|
+
}
|
|
52
|
+
// SDT.content is (Paragraph | Run | Table)[]. Recurse into its
|
|
53
|
+
// tables to find nested SDTs.
|
|
54
|
+
for (const c of block.content) {
|
|
55
|
+
if (c.type === "table") {
|
|
56
|
+
visit([c]);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
else if (block.type === "table") {
|
|
61
|
+
for (const row of block.rows) {
|
|
62
|
+
for (const cell of row.cells) {
|
|
63
|
+
visit(cell.content);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
visit(doc.body);
|
|
70
|
+
if (doc.headers) {
|
|
71
|
+
for (const [, h] of doc.headers) {
|
|
72
|
+
visit(h.content.children);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
if (doc.footers) {
|
|
76
|
+
for (const [, f] of doc.footers) {
|
|
77
|
+
visit(f.content.children);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return max;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Recursively collect hyperlinks with a URL (no `rId` yet) from body content.
|
|
84
|
+
*/
|
|
85
|
+
function collectHyperlinks(body) {
|
|
86
|
+
const links = [];
|
|
87
|
+
walkBlocks(body, {
|
|
88
|
+
enterParagraph(p) {
|
|
89
|
+
scanChildrenForHyperlinks(p.children, links);
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
return links;
|
|
93
|
+
}
|
|
94
|
+
/** Infer a content type for an opaque part based on its file extension. */
|
|
95
|
+
function inferContentType(ext) {
|
|
96
|
+
const map = {
|
|
97
|
+
xml: "application/xml",
|
|
98
|
+
rels: "application/vnd.openxmlformats-package.relationships+xml",
|
|
99
|
+
png: "image/png",
|
|
100
|
+
jpeg: "image/jpeg",
|
|
101
|
+
jpg: "image/jpeg",
|
|
102
|
+
gif: "image/gif",
|
|
103
|
+
bmp: "image/bmp",
|
|
104
|
+
tiff: "image/tiff",
|
|
105
|
+
tif: "image/tiff",
|
|
106
|
+
svg: "image/svg+xml",
|
|
107
|
+
webp: "image/webp",
|
|
108
|
+
emf: "image/x-emf",
|
|
109
|
+
wmf: "image/x-wmf",
|
|
110
|
+
odttf: "application/vnd.openxmlformats-officedocument.obfuscatedFont",
|
|
111
|
+
ttf: "application/x-font-ttf",
|
|
112
|
+
otf: "application/x-font-otf",
|
|
113
|
+
bin: "application/vnd.openxmlformats-officedocument.oleObject",
|
|
114
|
+
vml: "application/vnd.openxmlformats-officedocument.vmlDrawing"
|
|
115
|
+
};
|
|
116
|
+
return map[ext];
|
|
117
|
+
}
|
|
118
|
+
/** Resolve the main document part content type based on docType. */
|
|
119
|
+
function resolveDocumentContentType(doc) {
|
|
120
|
+
switch (doc.docType) {
|
|
121
|
+
case "template":
|
|
122
|
+
return ContentType.Template;
|
|
123
|
+
case "macroEnabledDocument":
|
|
124
|
+
return ContentType.DocumentMacroEnabled;
|
|
125
|
+
case "macroEnabledTemplate":
|
|
126
|
+
return ContentType.TemplateMacroEnabled;
|
|
127
|
+
default:
|
|
128
|
+
return ContentType.Document;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Package a DocxDocument model into a DOCX ZIP file.
|
|
133
|
+
* Returns the ZIP bytes as Uint8Array.
|
|
134
|
+
*
|
|
135
|
+
* This function does NOT modify the input `doc` object. Internally it creates
|
|
136
|
+
* shallow copies of mutable structures (images, headers, footers, etc.) to
|
|
137
|
+
* assign relationship IDs without polluting the caller's model.
|
|
138
|
+
*/
|
|
139
|
+
export async function packageDocx(doc, optionsOrCompressionLevel) {
|
|
140
|
+
const options = typeof optionsOrCompressionLevel === "number"
|
|
141
|
+
? { compressionLevel: optionsOrCompressionLevel }
|
|
142
|
+
: (optionsOrCompressionLevel ?? {});
|
|
143
|
+
// Create a working copy so we never mutate the caller's doc
|
|
144
|
+
const workDoc = shallowCopyDocForPackaging(doc);
|
|
145
|
+
return _packageDocxInner(workDoc, options);
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Create a shallow working copy of the document parts that packageDocx still
|
|
149
|
+
* mutates (header/footer/altChunk/hyperlink rId injection). Binary data
|
|
150
|
+
* (Uint8Array) is shared, not cloned. The image table is intentionally NOT
|
|
151
|
+
* cloned: packaging never rewrites image rIds anymore — collisions are now
|
|
152
|
+
* handled via a render-context remap (see `imageRemap` below).
|
|
153
|
+
*/
|
|
154
|
+
function shallowCopyDocForPackaging(doc) {
|
|
155
|
+
// Shallow-copy body so we can wrap floating images / altChunks without
|
|
156
|
+
// touching the caller's array.
|
|
157
|
+
const body = doc.body.map(item => {
|
|
158
|
+
if (item.type === "floatingImage" || item.type === "altChunk") {
|
|
159
|
+
return { ...item };
|
|
160
|
+
}
|
|
161
|
+
return item;
|
|
162
|
+
});
|
|
163
|
+
// Shallow-copy headers/footers maps (their `rId` is still assigned in place
|
|
164
|
+
// for backward compatibility with downstream relationship lookups).
|
|
165
|
+
const headers = doc.headers
|
|
166
|
+
? new Map(Array.from(doc.headers.entries()).map(([k, v]) => [k, { ...v }]))
|
|
167
|
+
: undefined;
|
|
168
|
+
const footers = doc.footers
|
|
169
|
+
? new Map(Array.from(doc.footers.entries()).map(([k, v]) => [k, { ...v }]))
|
|
170
|
+
: undefined;
|
|
171
|
+
// Sanitize media/font file names. The fileName attribute eventually
|
|
172
|
+
// becomes a ZIP entry name (`word/media/...`, `word/fonts/...`); a
|
|
173
|
+
// hostile DOCX read in via `readDocx` could carry a fileName like
|
|
174
|
+
// `../../etc/passwd.png`, which without sanitisation would produce a
|
|
175
|
+
// ZIP entry that escapes the package root when consumers naively
|
|
176
|
+
// unpack it (zipslip). Allocate fresh leaf names that are unique
|
|
177
|
+
// within their respective collections so two attacker-supplied names
|
|
178
|
+
// sanitising to the same string don't collide and silently overwrite
|
|
179
|
+
// each other.
|
|
180
|
+
const sanitizeImages = (images) => {
|
|
181
|
+
if (!images || images.length === 0) {
|
|
182
|
+
return images;
|
|
183
|
+
}
|
|
184
|
+
const usedNames = new Set();
|
|
185
|
+
let mutated = false;
|
|
186
|
+
const result = images.map(img => {
|
|
187
|
+
const safe = uniqueSanitizedName(img.fileName, usedNames, "image.bin");
|
|
188
|
+
if (safe !== img.fileName) {
|
|
189
|
+
mutated = true;
|
|
190
|
+
return { ...img, fileName: safe };
|
|
191
|
+
}
|
|
192
|
+
return img;
|
|
193
|
+
});
|
|
194
|
+
return mutated ? result : images;
|
|
195
|
+
};
|
|
196
|
+
const sanitizeFonts = (fonts) => {
|
|
197
|
+
if (!fonts || fonts.length === 0) {
|
|
198
|
+
return fonts;
|
|
199
|
+
}
|
|
200
|
+
const usedNames = new Set();
|
|
201
|
+
let mutated = false;
|
|
202
|
+
const result = fonts.map(ef => {
|
|
203
|
+
const safe = uniqueSanitizedName(ef.fileName, usedNames, "font.bin");
|
|
204
|
+
if (safe !== ef.fileName) {
|
|
205
|
+
mutated = true;
|
|
206
|
+
return { ...ef, fileName: safe };
|
|
207
|
+
}
|
|
208
|
+
return ef;
|
|
209
|
+
});
|
|
210
|
+
return mutated ? result : fonts;
|
|
211
|
+
};
|
|
212
|
+
return {
|
|
213
|
+
...doc,
|
|
214
|
+
body,
|
|
215
|
+
headers,
|
|
216
|
+
footers,
|
|
217
|
+
images: sanitizeImages(doc.images),
|
|
218
|
+
embeddedFonts: sanitizeFonts(doc.embeddedFonts)
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* Returns the sanitised leaf name. If the cleaned form would collide
|
|
223
|
+
* with an entry already in `used`, a numeric suffix is appended until
|
|
224
|
+
* unique. The chosen name is added to `used`.
|
|
225
|
+
*/
|
|
226
|
+
function uniqueSanitizedName(raw, used, fallback) {
|
|
227
|
+
let candidate = sanitizeMediaFileName(raw, fallback);
|
|
228
|
+
if (used.has(candidate)) {
|
|
229
|
+
const dot = candidate.lastIndexOf(".");
|
|
230
|
+
const stem = dot >= 0 ? candidate.slice(0, dot) : candidate;
|
|
231
|
+
const ext = dot >= 0 ? candidate.slice(dot) : "";
|
|
232
|
+
let n = 2;
|
|
233
|
+
while (used.has(`${stem}_${n}${ext}`)) {
|
|
234
|
+
n++;
|
|
235
|
+
}
|
|
236
|
+
candidate = `${stem}_${n}${ext}`;
|
|
237
|
+
}
|
|
238
|
+
used.add(candidate);
|
|
239
|
+
return candidate;
|
|
240
|
+
}
|
|
241
|
+
async function _packageDocxInner(doc, options) {
|
|
242
|
+
const archive = zip({ level: options.compressionLevel ?? 6 });
|
|
243
|
+
const securityPolicy = resolveSecurityPolicy(options.securityPolicy);
|
|
244
|
+
const rawXmlPolicy = securityPolicy.rawXmlPolicy;
|
|
245
|
+
// Managers
|
|
246
|
+
const contentTypes = createContentTypes();
|
|
247
|
+
const packageRels = createRelationships();
|
|
248
|
+
const documentRels = createRelationships();
|
|
249
|
+
// --- Package relationships ---
|
|
250
|
+
addRelationship(packageRels, RelType.OfficeDocument, "word/document.xml");
|
|
251
|
+
addRelationship(packageRels, RelType.CoreProperties, "docProps/core.xml");
|
|
252
|
+
addRelationship(packageRels, RelType.ExtendedProperties, "docProps/app.xml");
|
|
253
|
+
// Custom properties
|
|
254
|
+
const hasCustomProps = doc.customProperties && doc.customProperties.length > 0;
|
|
255
|
+
if (hasCustomProps) {
|
|
256
|
+
addRelationship(packageRels, RelType.CustomProperties, "docProps/custom.xml");
|
|
257
|
+
}
|
|
258
|
+
// --- Document relationships ---
|
|
259
|
+
addRelationship(documentRels, RelType.Styles, "styles.xml");
|
|
260
|
+
addRelationship(documentRels, RelType.Settings, "settings.xml");
|
|
261
|
+
addRelationship(documentRels, RelType.FontTable, "fontTable.xml");
|
|
262
|
+
addRelationship(documentRels, RelType.Theme, "theme/theme1.xml");
|
|
263
|
+
// Numbering
|
|
264
|
+
const hasNumbering = (doc.abstractNumberings && doc.abstractNumberings.length > 0) ||
|
|
265
|
+
(doc.numberingInstances && doc.numberingInstances.length > 0);
|
|
266
|
+
if (hasNumbering) {
|
|
267
|
+
addRelationship(documentRels, RelType.Numbering, "numbering.xml");
|
|
268
|
+
}
|
|
269
|
+
// Footnotes & Endnotes
|
|
270
|
+
const hasFootnotes = doc.footnotes && doc.footnotes.length > 0;
|
|
271
|
+
const hasEndnotes = doc.endnotes && doc.endnotes.length > 0;
|
|
272
|
+
if (hasFootnotes) {
|
|
273
|
+
addRelationship(documentRels, RelType.Footnotes, "footnotes.xml");
|
|
274
|
+
}
|
|
275
|
+
if (hasEndnotes) {
|
|
276
|
+
addRelationship(documentRels, RelType.Endnotes, "endnotes.xml");
|
|
277
|
+
}
|
|
278
|
+
// Comments
|
|
279
|
+
const hasComments = doc.comments && doc.comments.length > 0;
|
|
280
|
+
if (hasComments) {
|
|
281
|
+
addRelationship(documentRels, RelType.Comments, "comments.xml");
|
|
282
|
+
}
|
|
283
|
+
// Images
|
|
284
|
+
//
|
|
285
|
+
// The model's `image.rId` is the authoritative reference: it is what every
|
|
286
|
+
// inline/floating drawing in the body uses, so the easiest way to keep
|
|
287
|
+
// r:embed and the .rels file in sync is to register relationships under that
|
|
288
|
+
// exact id. If a model rId happens to clash with a previously-registered
|
|
289
|
+
// relationship in this same .rels file (e.g. styles → "rId1" while a model
|
|
290
|
+
// image also asks for "rId1"), we fall back to allocating a fresh rId and
|
|
291
|
+
// record the substitution in `imageRemap`. Writers consult that map via
|
|
292
|
+
// ctx.imageRIdRemap when emitting r:embed so the body and the .rels file
|
|
293
|
+
// stay consistent.
|
|
294
|
+
//
|
|
295
|
+
// For SVG with raster fallback, the model rId points at the PNG fallback
|
|
296
|
+
// resource (so r:embed on a:blip targets a raster image — the legacy wire
|
|
297
|
+
// shape Word readers expect). We additionally register the SVG itself
|
|
298
|
+
// under a secondary rId. We then surface that secondary rId on the
|
|
299
|
+
// inline/floating image's `svgRId` field — but only on shallow copies that
|
|
300
|
+
// live inside the work doc, so the caller's input model is never mutated.
|
|
301
|
+
const imageExtensions = new Set();
|
|
302
|
+
const imageRemap = new Map(); // model rId -> registered rId (only set on collision)
|
|
303
|
+
const imageByRid = new Map(); // model rId -> ImageDef
|
|
304
|
+
const svgFallbacks = [];
|
|
305
|
+
// For each main image rId that has a SVG fallback, the secondary rId
|
|
306
|
+
// pointing at the SVG file. Used to populate svgRId on inline/floating
|
|
307
|
+
// image content when the source model didn't already provide one.
|
|
308
|
+
const imageSvgRIdMap = new Map();
|
|
309
|
+
if (doc.images) {
|
|
310
|
+
for (const img of doc.images) {
|
|
311
|
+
if (img.rId) {
|
|
312
|
+
imageByRid.set(img.rId, img);
|
|
313
|
+
}
|
|
314
|
+
// Index alias rIds so that header/footer-local references resolve to
|
|
315
|
+
// the same ImageDef. Aliases live in their own .rels id space, so when
|
|
316
|
+
// we register them later we use the alias rId verbatim.
|
|
317
|
+
if (img.aliasRIds) {
|
|
318
|
+
for (const alias of img.aliasRIds) {
|
|
319
|
+
if (!imageByRid.has(alias)) {
|
|
320
|
+
imageByRid.set(alias, img);
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
const registerImageRel = (modelRId, target) => {
|
|
326
|
+
if (!modelRId) {
|
|
327
|
+
return addRelationship(documentRels, RelType.Image, target);
|
|
328
|
+
}
|
|
329
|
+
// OOXML / OPC permits any xs:ID as a Relationship Id, but
|
|
330
|
+
// Microsoft Word enforces the conventional `rId<N>` shape and
|
|
331
|
+
// rejects the package when it encounters anything else (e.g. the
|
|
332
|
+
// `__img_<N>` placeholders our builders emit). Allocate a fresh
|
|
333
|
+
// canonical rId whenever the model id departs from that pattern,
|
|
334
|
+
// and remap content references through `imageRIdRemap`.
|
|
335
|
+
const isCanonicalRId = /^rId\d+$/.test(modelRId);
|
|
336
|
+
if (!isCanonicalRId || documentRels.hasId(modelRId)) {
|
|
337
|
+
const newId = addRelationship(documentRels, RelType.Image, target);
|
|
338
|
+
imageRemap.set(modelRId, newId);
|
|
339
|
+
return newId;
|
|
340
|
+
}
|
|
341
|
+
addRelationshipWithId(documentRels, modelRId, RelType.Image, target);
|
|
342
|
+
return modelRId;
|
|
343
|
+
};
|
|
344
|
+
for (const img of doc.images) {
|
|
345
|
+
const oldRid = img.rId;
|
|
346
|
+
if (img.mediaType === "svg" && img.fallbackData) {
|
|
347
|
+
// Main rId points at the PNG fallback (the raster image consumed by
|
|
348
|
+
// a:blip).
|
|
349
|
+
const baseName = img.fileName.replace(/\.[^.]+$/, "");
|
|
350
|
+
const fallbackFileName = `${baseName}_fallback.png`;
|
|
351
|
+
registerImageRel(oldRid, `media/${fallbackFileName}`);
|
|
352
|
+
imageExtensions.add("png");
|
|
353
|
+
// Secondary rId for the actual SVG; auto-allocated.
|
|
354
|
+
const svgRId = addRelationship(documentRels, RelType.Image, `media/${img.fileName}`);
|
|
355
|
+
if (oldRid) {
|
|
356
|
+
imageSvgRIdMap.set(oldRid, svgRId);
|
|
357
|
+
}
|
|
358
|
+
const ext = getFileExt(img.fileName);
|
|
359
|
+
if (ext) {
|
|
360
|
+
imageExtensions.add(ext);
|
|
361
|
+
}
|
|
362
|
+
svgFallbacks.push({ fallbackFileName, data: img.fallbackData });
|
|
363
|
+
}
|
|
364
|
+
else {
|
|
365
|
+
registerImageRel(oldRid, `media/${img.fileName}`);
|
|
366
|
+
const ext = getFileExt(img.fileName);
|
|
367
|
+
if (ext) {
|
|
368
|
+
imageExtensions.add(ext);
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
// Populate svgRId on inline/floating image content nodes for SVG fallbacks.
|
|
374
|
+
// We only mutate shallow copies inside the working doc — never the caller's
|
|
375
|
+
// input. This is invisible to readers that already supplied svgRId.
|
|
376
|
+
if (imageSvgRIdMap.size > 0) {
|
|
377
|
+
const populateSvg = (target) => {
|
|
378
|
+
if (target.svgRId) {
|
|
379
|
+
return;
|
|
380
|
+
}
|
|
381
|
+
const svgRId = imageSvgRIdMap.get(target.rId);
|
|
382
|
+
if (svgRId) {
|
|
383
|
+
target.svgRId = svgRId;
|
|
384
|
+
}
|
|
385
|
+
};
|
|
386
|
+
// Walk the work-doc body. Floating images and altChunks were shallow-copied
|
|
387
|
+
// by shallowCopyDocForPackaging; for paragraphs we lazily clone the run
|
|
388
|
+
// content nodes that carry inline SVG references.
|
|
389
|
+
const processBody = (blocks) => {
|
|
390
|
+
return blocks.map(block => {
|
|
391
|
+
if (block.type === "floatingImage") {
|
|
392
|
+
if (imageSvgRIdMap.has(block.rId)) {
|
|
393
|
+
populateSvg(block);
|
|
394
|
+
}
|
|
395
|
+
return block;
|
|
396
|
+
}
|
|
397
|
+
if (block.type === "paragraph") {
|
|
398
|
+
let paragraphCopied = null;
|
|
399
|
+
let childrenCopied = null;
|
|
400
|
+
for (let i = 0; i < block.children.length; i++) {
|
|
401
|
+
const child = block.children[i];
|
|
402
|
+
if (!isRun(child)) {
|
|
403
|
+
continue;
|
|
404
|
+
}
|
|
405
|
+
const run = child;
|
|
406
|
+
let runCopied = null;
|
|
407
|
+
let contentCopied = null;
|
|
408
|
+
for (let j = 0; j < run.content.length; j++) {
|
|
409
|
+
const c = run.content[j];
|
|
410
|
+
if (c.type !== "image" || !c.rId) {
|
|
411
|
+
continue;
|
|
412
|
+
}
|
|
413
|
+
if (!imageSvgRIdMap.has(c.rId) || c.svgRId) {
|
|
414
|
+
continue;
|
|
415
|
+
}
|
|
416
|
+
if (!contentCopied) {
|
|
417
|
+
contentCopied = [...run.content];
|
|
418
|
+
}
|
|
419
|
+
const cloned = { ...c };
|
|
420
|
+
populateSvg(cloned);
|
|
421
|
+
contentCopied[j] = cloned;
|
|
422
|
+
}
|
|
423
|
+
if (contentCopied) {
|
|
424
|
+
runCopied = { ...run, content: contentCopied };
|
|
425
|
+
if (!childrenCopied) {
|
|
426
|
+
childrenCopied = [...block.children];
|
|
427
|
+
}
|
|
428
|
+
childrenCopied[i] = runCopied;
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
if (childrenCopied) {
|
|
432
|
+
paragraphCopied = { ...block, children: childrenCopied };
|
|
433
|
+
return paragraphCopied;
|
|
434
|
+
}
|
|
435
|
+
return block;
|
|
436
|
+
}
|
|
437
|
+
if (block.type === "table") {
|
|
438
|
+
const newRows = block.rows.map(row => ({
|
|
439
|
+
...row,
|
|
440
|
+
cells: row.cells.map(cell => ({
|
|
441
|
+
...cell,
|
|
442
|
+
content: processBody(cell.content)
|
|
443
|
+
}))
|
|
444
|
+
}));
|
|
445
|
+
return { ...block, rows: newRows };
|
|
446
|
+
}
|
|
447
|
+
return block;
|
|
448
|
+
});
|
|
449
|
+
};
|
|
450
|
+
doc.body = processBody(doc.body);
|
|
451
|
+
// Same treatment for header/footer content (each is essentially a body
|
|
452
|
+
// fragment). headers/footers maps were shallow-copied by
|
|
453
|
+
// shallowCopyDocForPackaging, so swapping `content.children` is safe.
|
|
454
|
+
const rewriteHeaderFooter = (defs) => {
|
|
455
|
+
if (!defs) {
|
|
456
|
+
return;
|
|
457
|
+
}
|
|
458
|
+
for (const [, def] of defs) {
|
|
459
|
+
const newChildren = processBody(def.content.children);
|
|
460
|
+
if (newChildren !== def.content.children) {
|
|
461
|
+
def.content = {
|
|
462
|
+
...def.content,
|
|
463
|
+
children: newChildren
|
|
464
|
+
};
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
};
|
|
468
|
+
rewriteHeaderFooter(doc.headers);
|
|
469
|
+
rewriteHeaderFooter(doc.footers);
|
|
470
|
+
// Footnotes and endnotes carry their own paragraph lists.
|
|
471
|
+
const rewriteNotes = (notes) => {
|
|
472
|
+
if (!notes || notes.length === 0) {
|
|
473
|
+
return notes;
|
|
474
|
+
}
|
|
475
|
+
let changed = false;
|
|
476
|
+
const out = notes.map(note => {
|
|
477
|
+
const newContent = processBody(note.content);
|
|
478
|
+
if (newContent === note.content) {
|
|
479
|
+
return note;
|
|
480
|
+
}
|
|
481
|
+
changed = true;
|
|
482
|
+
return { ...note, content: newContent };
|
|
483
|
+
});
|
|
484
|
+
return changed ? out : notes;
|
|
485
|
+
};
|
|
486
|
+
const newFootnotes = rewriteNotes(doc.footnotes);
|
|
487
|
+
if (newFootnotes !== doc.footnotes) {
|
|
488
|
+
doc.footnotes = newFootnotes;
|
|
489
|
+
}
|
|
490
|
+
const newEndnotes = rewriteNotes(doc.endnotes);
|
|
491
|
+
if (newEndnotes !== doc.endnotes) {
|
|
492
|
+
doc.endnotes = newEndnotes;
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
// Hyperlinks (external).
|
|
496
|
+
// We register relationships under the model's existing rId where possible,
|
|
497
|
+
// and stash newly-allocated rIds on a WeakMap keyed by the Hyperlink object
|
|
498
|
+
// so the renderer can look them up without us writing onto the caller's
|
|
499
|
+
// model. The map lives on the render context.
|
|
500
|
+
//
|
|
501
|
+
// Body, footnotes, endnotes, headers and footers each render against a
|
|
502
|
+
// *different* OPC part, so each part has its own .rels namespace. Using one
|
|
503
|
+
// WeakMap is fine because Hyperlink objects are unique by identity, but the
|
|
504
|
+
// **registration** has to happen against the correct rel manager — we register
|
|
505
|
+
// body hyperlinks into documentRels here, and footnote/endnote/header/footer
|
|
506
|
+
// hyperlinks into their own rel managers below.
|
|
507
|
+
const hyperlinkRIds = new WeakMap();
|
|
508
|
+
const hyperlinks = collectHyperlinks(doc.body);
|
|
509
|
+
for (const link of hyperlinks) {
|
|
510
|
+
if (link.url) {
|
|
511
|
+
// Sanitize: drop dangerous schemes (javascript:, vbscript:, data:, …)
|
|
512
|
+
// before they reach document.xml.rels. The renderer will simply emit
|
|
513
|
+
// the surrounding `<w:hyperlink>` without an `r:id`, which keeps the
|
|
514
|
+
// inner runs but does not produce a clickable target.
|
|
515
|
+
const safe = sanitizeUrl(link.url);
|
|
516
|
+
if (!safe) {
|
|
517
|
+
continue;
|
|
518
|
+
}
|
|
519
|
+
const rId = addRelationship(documentRels, RelType.Hyperlink, safe, "External");
|
|
520
|
+
hyperlinkRIds.set(link, rId);
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
// Footnotes/endnotes have independent .rels parts. Build them lazily so we
|
|
524
|
+
// only emit a footnotes.xml.rels file when there's something to register.
|
|
525
|
+
const footnoteRels = createRelationships();
|
|
526
|
+
const endnoteRels = createRelationships();
|
|
527
|
+
const registerNoteHyperlinks = (notes, rels) => {
|
|
528
|
+
if (!notes) {
|
|
529
|
+
return;
|
|
530
|
+
}
|
|
531
|
+
for (const note of notes) {
|
|
532
|
+
const links = [];
|
|
533
|
+
for (const p of note.content) {
|
|
534
|
+
scanChildrenForHyperlinks(p.children, links);
|
|
535
|
+
}
|
|
536
|
+
for (const link of links) {
|
|
537
|
+
if (link.url) {
|
|
538
|
+
const safe = sanitizeUrl(link.url);
|
|
539
|
+
if (!safe) {
|
|
540
|
+
continue;
|
|
541
|
+
}
|
|
542
|
+
const rId = addRelationship(rels, RelType.Hyperlink, safe, "External");
|
|
543
|
+
hyperlinkRIds.set(link, rId);
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
};
|
|
548
|
+
registerNoteHyperlinks(doc.footnotes, footnoteRels);
|
|
549
|
+
registerNoteHyperlinks(doc.endnotes, endnoteRels);
|
|
550
|
+
// Footnotes/endnotes may also reference images. Register those references
|
|
551
|
+
// using the same model rId that the run content emits so footnotes.xml's
|
|
552
|
+
// r:embed resolves locally.
|
|
553
|
+
const registerNoteImages = (notes, rels) => {
|
|
554
|
+
if (!notes || !doc.images) {
|
|
555
|
+
return;
|
|
556
|
+
}
|
|
557
|
+
const seen = new Set();
|
|
558
|
+
for (const note of notes) {
|
|
559
|
+
for (const p of note.content) {
|
|
560
|
+
const rIds = new Set();
|
|
561
|
+
scanChildrenForImages(p.children, rIds);
|
|
562
|
+
for (const oldRid of rIds) {
|
|
563
|
+
if (seen.has(oldRid)) {
|
|
564
|
+
continue;
|
|
565
|
+
}
|
|
566
|
+
seen.add(oldRid);
|
|
567
|
+
const img = imageByRid.get(oldRid);
|
|
568
|
+
if (img) {
|
|
569
|
+
addRelationshipWithId(rels, oldRid, RelType.Image, `media/${img.fileName}`);
|
|
570
|
+
// SVG fallback: also register the secondary svg rId locally if the
|
|
571
|
+
// model knows it (or if we allocated it). For notes we look up the
|
|
572
|
+
// already-allocated svgRId from imageSvgRIdMap if present.
|
|
573
|
+
const svgRId = imageSvgRIdMap.get(oldRid);
|
|
574
|
+
if (svgRId && !rels.hasId(svgRId)) {
|
|
575
|
+
addRelationshipWithId(rels, svgRId, RelType.Image, `media/${img.fileName}`);
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
};
|
|
582
|
+
registerNoteImages(doc.footnotes, footnoteRels);
|
|
583
|
+
registerNoteImages(doc.endnotes, endnoteRels);
|
|
584
|
+
// Comments have their own .rels part (word/_rels/comments.xml.rels).
|
|
585
|
+
// Hyperlinks/images referenced from inside comment content must register
|
|
586
|
+
// here, not in document.xml.rels — otherwise readers can't resolve them.
|
|
587
|
+
const commentRels = createRelationships();
|
|
588
|
+
if (doc.comments && doc.comments.length > 0) {
|
|
589
|
+
const commentBodies = doc.comments.map(c => ({ content: c.content }));
|
|
590
|
+
registerNoteHyperlinks(commentBodies, commentRels);
|
|
591
|
+
registerNoteImages(commentBodies, commentRels);
|
|
592
|
+
}
|
|
593
|
+
// Process altChunk body items: register relationship and prepare data part.
|
|
594
|
+
// We never mutate the chunk objects — the rId is published via a WeakMap
|
|
595
|
+
// that the renderer reads through the WordRenderContext. This matters for
|
|
596
|
+
// altChunks nested inside tables / SDTs which the shallow body copy did
|
|
597
|
+
// not (and intentionally cannot cheaply) clone.
|
|
598
|
+
const altChunkRIds = new WeakMap();
|
|
599
|
+
const altChunks = [];
|
|
600
|
+
collectAltChunks(doc.body, altChunks);
|
|
601
|
+
altChunks.forEach((chunk, i) => {
|
|
602
|
+
const fileName = chunk.fileName ?? `afchunk${i + 1}.html`;
|
|
603
|
+
// Register relationship for the alt chunk
|
|
604
|
+
const rId = addRelationship(documentRels, "http://schemas.openxmlformats.org/officeDocument/2006/relationships/aFChunk", fileName);
|
|
605
|
+
altChunkRIds.set(chunk, rId);
|
|
606
|
+
// Register content type
|
|
607
|
+
const ct = chunk.contentType ?? "text/html";
|
|
608
|
+
addContentTypeOverride(contentTypes, `/word/${fileName}`, ct);
|
|
609
|
+
// Write data to archive
|
|
610
|
+
if (chunk.data) {
|
|
611
|
+
archive.add(`word/${fileName}`, chunk.data);
|
|
612
|
+
}
|
|
613
|
+
});
|
|
614
|
+
// Headers
|
|
615
|
+
//
|
|
616
|
+
// For each header in the model we allocate a fresh document-level rId and
|
|
617
|
+
// (re)write the rId on the header definition. Two maps are also produced
|
|
618
|
+
// for later sectionProperties rewiring:
|
|
619
|
+
// - headerKeyToNewRid: keyed by the model's `headers` Map key (typically
|
|
620
|
+
// the original rId from a round-trip, or "default"/"first"/"even" from
|
|
621
|
+
// the builder).
|
|
622
|
+
// - headerOldRidToNewRid: keyed by any rId that previously identified
|
|
623
|
+
// this header (the map key, the value's pre-existing `rId` field) so
|
|
624
|
+
// references that still hold the old rId can be remapped.
|
|
625
|
+
let headerIndex = 1;
|
|
626
|
+
const headerRelManagers = new Map();
|
|
627
|
+
const headerKeyToNewRid = new Map();
|
|
628
|
+
const headerOldRidToNewRid = new Map();
|
|
629
|
+
// Map header `type` ("default" | "first" | "even") to its new rId, if the
|
|
630
|
+
// model only has one header per type. Used to fill in references that
|
|
631
|
+
// were created with a placeholder rId="" by callers that didn't yet know
|
|
632
|
+
// the rId (typical builder usage).
|
|
633
|
+
const headerTypeToNewRid = new Map();
|
|
634
|
+
if (doc.headers) {
|
|
635
|
+
for (const [key, headerDef] of doc.headers) {
|
|
636
|
+
const rId = addRelationship(documentRels, RelType.Header, `header${headerIndex}.xml`);
|
|
637
|
+
const oldRid = headerDef.rId;
|
|
638
|
+
headerDef.rId = rId;
|
|
639
|
+
headerKeyToNewRid.set(key, rId);
|
|
640
|
+
if (key) {
|
|
641
|
+
headerOldRidToNewRid.set(key, rId);
|
|
642
|
+
}
|
|
643
|
+
if (oldRid && oldRid !== rId) {
|
|
644
|
+
headerOldRidToNewRid.set(oldRid, rId);
|
|
645
|
+
}
|
|
646
|
+
// Heuristic: if the map key looks like a header type rather than an
|
|
647
|
+
// rId, also expose it via headerTypeToNewRid so empty-rId references
|
|
648
|
+
// ({ type: "default", rId: "" }) can be resolved by type.
|
|
649
|
+
if (key === "default" || key === "first" || key === "even" || key === "odd") {
|
|
650
|
+
// Last writer wins if the caller provided multiple headers with the
|
|
651
|
+
// same type — that's already an ambiguous model.
|
|
652
|
+
headerTypeToNewRid.set(key, rId);
|
|
653
|
+
}
|
|
654
|
+
// Create per-header relationship state for images and hyperlinks.
|
|
655
|
+
// Header/footer .rels is its own id space, so we register images using
|
|
656
|
+
// the model rId — that's also what the body XML emits via r:embed
|
|
657
|
+
// (header/footer XML never goes through imageRemap because the local
|
|
658
|
+
// .rels is independent from the document .rels).
|
|
659
|
+
const hRels = createRelationships();
|
|
660
|
+
const imgRids = collectImageRidsFromContent(headerDef.content);
|
|
661
|
+
if (imgRids.size > 0 && doc.images) {
|
|
662
|
+
for (const oldRid of imgRids) {
|
|
663
|
+
const img = imageByRid.get(oldRid);
|
|
664
|
+
if (img) {
|
|
665
|
+
addRelationshipWithId(hRels, oldRid, RelType.Image, `media/${img.fileName}`);
|
|
666
|
+
// SVG fallback: register the secondary svg rId locally too so
|
|
667
|
+
// asvg:svgBlip in header XML resolves against this part's .rels.
|
|
668
|
+
const svgRId = imageSvgRIdMap.get(oldRid);
|
|
669
|
+
if (svgRId && !hRels.hasId(svgRId)) {
|
|
670
|
+
addRelationshipWithId(hRels, svgRId, RelType.Image, `media/${img.fileName}`);
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
}
|
|
674
|
+
}
|
|
675
|
+
// Hyperlinks in header
|
|
676
|
+
const hLinks = collectHyperlinksFromHeaderFooter(headerDef.content);
|
|
677
|
+
for (const link of hLinks) {
|
|
678
|
+
if (link.url) {
|
|
679
|
+
const safe = sanitizeUrl(link.url);
|
|
680
|
+
if (!safe) {
|
|
681
|
+
continue;
|
|
682
|
+
}
|
|
683
|
+
const linkRId = addRelationship(hRels, RelType.Hyperlink, safe, "External");
|
|
684
|
+
hyperlinkRIds.set(link, linkRId);
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
if (getRelationshipCount(hRels) > 0) {
|
|
688
|
+
headerRelManagers.set(key, hRels);
|
|
689
|
+
}
|
|
690
|
+
headerIndex++;
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
// Footers
|
|
694
|
+
let footerIndex = 1;
|
|
695
|
+
const footerRelManagers = new Map();
|
|
696
|
+
const footerKeyToNewRid = new Map();
|
|
697
|
+
const footerOldRidToNewRid = new Map();
|
|
698
|
+
const footerTypeToNewRid = new Map();
|
|
699
|
+
if (doc.footers) {
|
|
700
|
+
for (const [key, footerDef] of doc.footers) {
|
|
701
|
+
const rId = addRelationship(documentRels, RelType.Footer, `footer${footerIndex}.xml`);
|
|
702
|
+
const oldRid = footerDef.rId;
|
|
703
|
+
footerDef.rId = rId;
|
|
704
|
+
footerKeyToNewRid.set(key, rId);
|
|
705
|
+
if (key) {
|
|
706
|
+
footerOldRidToNewRid.set(key, rId);
|
|
707
|
+
}
|
|
708
|
+
if (oldRid && oldRid !== rId) {
|
|
709
|
+
footerOldRidToNewRid.set(oldRid, rId);
|
|
710
|
+
}
|
|
711
|
+
if (key === "default" || key === "first" || key === "even" || key === "odd") {
|
|
712
|
+
footerTypeToNewRid.set(key, rId);
|
|
713
|
+
}
|
|
714
|
+
// Create per-footer relationship state for images and hyperlinks.
|
|
715
|
+
const fRels = createRelationships();
|
|
716
|
+
const imgRids = collectImageRidsFromContent(footerDef.content);
|
|
717
|
+
if (imgRids.size > 0 && doc.images) {
|
|
718
|
+
for (const oldRid of imgRids) {
|
|
719
|
+
const img = imageByRid.get(oldRid);
|
|
720
|
+
if (img) {
|
|
721
|
+
addRelationshipWithId(fRels, oldRid, RelType.Image, `media/${img.fileName}`);
|
|
722
|
+
// SVG fallback: register the secondary svg rId locally too.
|
|
723
|
+
const svgRId = imageSvgRIdMap.get(oldRid);
|
|
724
|
+
if (svgRId && !fRels.hasId(svgRId)) {
|
|
725
|
+
addRelationshipWithId(fRels, svgRId, RelType.Image, `media/${img.fileName}`);
|
|
726
|
+
}
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
// Hyperlinks in footer
|
|
731
|
+
const fLinks = collectHyperlinksFromHeaderFooter(footerDef.content);
|
|
732
|
+
for (const link of fLinks) {
|
|
733
|
+
if (link.url) {
|
|
734
|
+
const safe = sanitizeUrl(link.url);
|
|
735
|
+
if (!safe) {
|
|
736
|
+
continue;
|
|
737
|
+
}
|
|
738
|
+
const linkRId = addRelationship(fRels, RelType.Hyperlink, safe, "External");
|
|
739
|
+
hyperlinkRIds.set(link, linkRId);
|
|
740
|
+
}
|
|
741
|
+
}
|
|
742
|
+
if (getRelationshipCount(fRels) > 0) {
|
|
743
|
+
footerRelManagers.set(key, fRels);
|
|
744
|
+
}
|
|
745
|
+
footerIndex++;
|
|
746
|
+
}
|
|
747
|
+
}
|
|
748
|
+
// Watermark header (auto-generated if doc.watermark is set)
|
|
749
|
+
let watermarkHeaderIndex;
|
|
750
|
+
let watermarkHeaderRels;
|
|
751
|
+
let watermarkHeaderRId;
|
|
752
|
+
if (doc.watermark) {
|
|
753
|
+
// Use next header index
|
|
754
|
+
const wmHdrIdx = headerIndex;
|
|
755
|
+
watermarkHeaderRId = addRelationship(documentRels, RelType.Header, `header${wmHdrIdx}.xml`);
|
|
756
|
+
watermarkHeaderIndex = wmHdrIdx;
|
|
757
|
+
// If image watermark, add image relationship in header .rels.
|
|
758
|
+
// Watermark .rels live in their own id space, but Word still expects
|
|
759
|
+
// the conventional `rId<N>` form. Allocate a fresh canonical id
|
|
760
|
+
// (preferring an `addRelationship` auto-allocation) when the model
|
|
761
|
+
// supplied a non-canonical id like `__img_1`.
|
|
762
|
+
if (doc.watermark.type === "image") {
|
|
763
|
+
const wmRels = createRelationships();
|
|
764
|
+
const wmRId = doc.watermark.rId;
|
|
765
|
+
const img = imageByRid.get(wmRId);
|
|
766
|
+
if (img) {
|
|
767
|
+
const isCanonical = /^rId\d+$/.test(wmRId);
|
|
768
|
+
if (isCanonical) {
|
|
769
|
+
addRelationshipWithId(wmRels, wmRId, RelType.Image, `media/${img.fileName}`);
|
|
770
|
+
}
|
|
771
|
+
else {
|
|
772
|
+
// Allocate a fresh rId<N> in the header .rels and remap the
|
|
773
|
+
// model id so the header writer emits the right r:embed.
|
|
774
|
+
const allocated = addRelationship(wmRels, RelType.Image, `media/${img.fileName}`);
|
|
775
|
+
imageRemap.set(wmRId, allocated);
|
|
776
|
+
}
|
|
777
|
+
}
|
|
778
|
+
watermarkHeaderRels = wmRels;
|
|
779
|
+
}
|
|
780
|
+
headerIndex++;
|
|
781
|
+
}
|
|
782
|
+
// --- Content Types ---
|
|
783
|
+
addImageContentTypeDefaults(contentTypes, imageExtensions);
|
|
784
|
+
// Determine main document content type based on docType
|
|
785
|
+
const mainDocContentType = resolveDocumentContentType(doc);
|
|
786
|
+
addContentTypeOverride(contentTypes, PartPath.Document, mainDocContentType);
|
|
787
|
+
addContentTypeOverride(contentTypes, PartPath.Styles, ContentType.Styles);
|
|
788
|
+
addContentTypeOverride(contentTypes, PartPath.Settings, ContentType.Settings);
|
|
789
|
+
addContentTypeOverride(contentTypes, PartPath.FontTable, ContentType.FontTable);
|
|
790
|
+
addContentTypeOverride(contentTypes, PartPath.Theme, ContentType.Theme);
|
|
791
|
+
if (hasNumbering) {
|
|
792
|
+
addContentTypeOverride(contentTypes, PartPath.Numbering, ContentType.Numbering);
|
|
793
|
+
}
|
|
794
|
+
if (hasFootnotes) {
|
|
795
|
+
addContentTypeOverride(contentTypes, PartPath.Footnotes, ContentType.Footnotes);
|
|
796
|
+
}
|
|
797
|
+
if (hasEndnotes) {
|
|
798
|
+
addContentTypeOverride(contentTypes, PartPath.Endnotes, ContentType.Endnotes);
|
|
799
|
+
}
|
|
800
|
+
if (hasComments) {
|
|
801
|
+
addContentTypeOverride(contentTypes, PartPath.Comments, ContentType.Comments);
|
|
802
|
+
}
|
|
803
|
+
headerIndex = 1;
|
|
804
|
+
if (doc.headers) {
|
|
805
|
+
for (const [,] of doc.headers) {
|
|
806
|
+
addContentTypeOverride(contentTypes, PartPath.header(headerIndex), ContentType.Header);
|
|
807
|
+
headerIndex++;
|
|
808
|
+
}
|
|
809
|
+
}
|
|
810
|
+
footerIndex = 1;
|
|
811
|
+
if (doc.footers) {
|
|
812
|
+
for (const [,] of doc.footers) {
|
|
813
|
+
addContentTypeOverride(contentTypes, PartPath.footer(footerIndex), ContentType.Footer);
|
|
814
|
+
footerIndex++;
|
|
815
|
+
}
|
|
816
|
+
}
|
|
817
|
+
if (watermarkHeaderIndex !== undefined) {
|
|
818
|
+
addContentTypeOverride(contentTypes, PartPath.header(watermarkHeaderIndex), ContentType.Header);
|
|
819
|
+
}
|
|
820
|
+
addContentTypeOverride(contentTypes, PartPath.CoreProps, ContentType.CoreProperties);
|
|
821
|
+
addContentTypeOverride(contentTypes, PartPath.AppProps, ContentType.ExtendedProperties);
|
|
822
|
+
if (hasCustomProps) {
|
|
823
|
+
addContentTypeOverride(contentTypes, PartPath.CustomProps, ContentType.CustomProperties);
|
|
824
|
+
}
|
|
825
|
+
// --- Generate & add parts to archive ---
|
|
826
|
+
// Note: [Content_Types].xml and _rels/.rels are serialized LAST so that any
|
|
827
|
+
// relationships/content types registered during content rendering (e.g.
|
|
828
|
+
// thumbnails, chart parts, alt chunks) are included.
|
|
829
|
+
// word/_rels/document.xml.rels is also deferred to include chart relationships.
|
|
830
|
+
// Build an effective doc that:
|
|
831
|
+
// (a) rewires sectionProperties header/footer references so their rId
|
|
832
|
+
// values match the document.xml.rels entries we just generated; and
|
|
833
|
+
// (b) includes the auto-generated watermark header reference.
|
|
834
|
+
// The caller's input doc is never mutated.
|
|
835
|
+
//
|
|
836
|
+
// Reference resolution order for each entry:
|
|
837
|
+
// 1. If `rId` is already a registered document-level header/footer rId,
|
|
838
|
+
// keep it as-is.
|
|
839
|
+
// 2. Otherwise try `headerOldRidToNewRid[rId]` (round-trip with the
|
|
840
|
+
// original rId still present, or the headers map keyed by old rId).
|
|
841
|
+
// 3. Otherwise fall back to `headerTypeToNewRid[type]` — this covers the
|
|
842
|
+
// common builder pattern where users add a header by `type` and use
|
|
843
|
+
// `rId: ""` as a placeholder.
|
|
844
|
+
// Same logic for footers.
|
|
845
|
+
const allowedHeaderRids = new Set(headerKeyToNewRid.values());
|
|
846
|
+
const allowedFooterRids = new Set(footerKeyToNewRid.values());
|
|
847
|
+
function resolveHeaderRef(ref) {
|
|
848
|
+
if (ref.rId && allowedHeaderRids.has(ref.rId)) {
|
|
849
|
+
return ref;
|
|
850
|
+
}
|
|
851
|
+
const remapped = ref.rId ? headerOldRidToNewRid.get(ref.rId) : undefined;
|
|
852
|
+
if (remapped) {
|
|
853
|
+
return { ...ref, rId: remapped };
|
|
854
|
+
}
|
|
855
|
+
const byType = headerTypeToNewRid.get(ref.type);
|
|
856
|
+
if (byType) {
|
|
857
|
+
return { ...ref, rId: byType };
|
|
858
|
+
}
|
|
859
|
+
// No header part matches this reference. Drop it rather than emit a
|
|
860
|
+
// dangling r:id that no .rels entry resolves.
|
|
861
|
+
return null;
|
|
862
|
+
}
|
|
863
|
+
function resolveFooterRef(ref) {
|
|
864
|
+
if (ref.rId && allowedFooterRids.has(ref.rId)) {
|
|
865
|
+
return ref;
|
|
866
|
+
}
|
|
867
|
+
const remapped = ref.rId ? footerOldRidToNewRid.get(ref.rId) : undefined;
|
|
868
|
+
if (remapped) {
|
|
869
|
+
return { ...ref, rId: remapped };
|
|
870
|
+
}
|
|
871
|
+
const byType = footerTypeToNewRid.get(ref.type);
|
|
872
|
+
if (byType) {
|
|
873
|
+
return { ...ref, rId: byType };
|
|
874
|
+
}
|
|
875
|
+
return null;
|
|
876
|
+
}
|
|
877
|
+
function rewireSectionRefs(sect) {
|
|
878
|
+
let next = sect;
|
|
879
|
+
let mutated = false;
|
|
880
|
+
if (sect.headers) {
|
|
881
|
+
const resolved = sect.headers
|
|
882
|
+
.map(resolveHeaderRef)
|
|
883
|
+
.filter((r) => r !== null);
|
|
884
|
+
if (resolved.length !== sect.headers.length ||
|
|
885
|
+
resolved.some((r, i) => r !== sect.headers[i])) {
|
|
886
|
+
next = { ...next, headers: resolved };
|
|
887
|
+
mutated = true;
|
|
888
|
+
}
|
|
889
|
+
}
|
|
890
|
+
if (sect.footers) {
|
|
891
|
+
const resolved = sect.footers
|
|
892
|
+
.map(resolveFooterRef)
|
|
893
|
+
.filter((r) => r !== null);
|
|
894
|
+
if (resolved.length !== sect.footers.length ||
|
|
895
|
+
resolved.some((r, i) => r !== sect.footers[i])) {
|
|
896
|
+
next = { ...next, footers: resolved };
|
|
897
|
+
mutated = true;
|
|
898
|
+
}
|
|
899
|
+
}
|
|
900
|
+
return mutated ? next : sect;
|
|
901
|
+
}
|
|
902
|
+
// Snapshot top-level sectionProperties (and apply rewiring). May be
|
|
903
|
+
// undefined when the document has no top-level section properties — we
|
|
904
|
+
// only synthesize one if there are headers/footers/watermark to reference.
|
|
905
|
+
let topLevelSect = doc.sectionProperties
|
|
906
|
+
? rewireSectionRefs(doc.sectionProperties)
|
|
907
|
+
: undefined;
|
|
908
|
+
// Auto-fill: if the model has headers/footers but no top-level reference
|
|
909
|
+
// was authored at all, synthesize references for each header/footer type
|
|
910
|
+
// so the section actually picks them up. This only fires when the section
|
|
911
|
+
// has zero header (or zero footer) refs — explicit author intent ("only
|
|
912
|
+
// first-page header is referenced") is preserved.
|
|
913
|
+
if (doc.headers &&
|
|
914
|
+
doc.headers.size > 0 &&
|
|
915
|
+
(!topLevelSect?.headers || topLevelSect.headers.length === 0)) {
|
|
916
|
+
const synthesized = [];
|
|
917
|
+
for (const [type, rId] of headerTypeToNewRid) {
|
|
918
|
+
synthesized.push({ type: type, rId });
|
|
919
|
+
}
|
|
920
|
+
if (synthesized.length > 0) {
|
|
921
|
+
topLevelSect = { ...(topLevelSect ?? {}), headers: synthesized };
|
|
922
|
+
}
|
|
923
|
+
}
|
|
924
|
+
if (doc.footers &&
|
|
925
|
+
doc.footers.size > 0 &&
|
|
926
|
+
(!topLevelSect?.footers || topLevelSect.footers.length === 0)) {
|
|
927
|
+
const synthesized = [];
|
|
928
|
+
for (const [type, rId] of footerTypeToNewRid) {
|
|
929
|
+
synthesized.push({ type: type, rId });
|
|
930
|
+
}
|
|
931
|
+
if (synthesized.length > 0) {
|
|
932
|
+
topLevelSect = { ...(topLevelSect ?? {}), footers: synthesized };
|
|
933
|
+
}
|
|
934
|
+
}
|
|
935
|
+
// Apply watermark reference (added after rewiring so its rId is preserved).
|
|
936
|
+
if (watermarkHeaderIndex !== undefined && watermarkHeaderRId) {
|
|
937
|
+
const existingHeaders = topLevelSect?.headers ? [...topLevelSect.headers] : [];
|
|
938
|
+
existingHeaders.push({ type: "default", rId: watermarkHeaderRId });
|
|
939
|
+
topLevelSect = { ...(topLevelSect ?? {}), headers: existingHeaders };
|
|
940
|
+
}
|
|
941
|
+
// Rewire sectionProperties embedded in body paragraphs (mid-document
|
|
942
|
+
// section breaks). Only rebuild the body array if any paragraph's section
|
|
943
|
+
// properties were actually rewritten.
|
|
944
|
+
const sectionRefRewriteNeeded = doc.body.some(item => item.type === "paragraph" &&
|
|
945
|
+
item.properties?.sectionProperties &&
|
|
946
|
+
((item.properties.sectionProperties.headers &&
|
|
947
|
+
item.properties.sectionProperties.headers.length > 0) ||
|
|
948
|
+
(item.properties.sectionProperties.footers &&
|
|
949
|
+
item.properties.sectionProperties.footers.length > 0)));
|
|
950
|
+
let effectiveBody = doc.body;
|
|
951
|
+
if (sectionRefRewriteNeeded) {
|
|
952
|
+
effectiveBody = doc.body.map(item => {
|
|
953
|
+
if (item.type !== "paragraph" || !item.properties?.sectionProperties) {
|
|
954
|
+
return item;
|
|
955
|
+
}
|
|
956
|
+
const rewired = rewireSectionRefs(item.properties.sectionProperties);
|
|
957
|
+
if (rewired === item.properties.sectionProperties) {
|
|
958
|
+
return item;
|
|
959
|
+
}
|
|
960
|
+
return {
|
|
961
|
+
...item,
|
|
962
|
+
properties: { ...item.properties, sectionProperties: rewired }
|
|
963
|
+
};
|
|
964
|
+
});
|
|
965
|
+
}
|
|
966
|
+
// Always rebuild effectiveDoc when we rewired anything, otherwise keep the
|
|
967
|
+
// original reference to avoid spurious shallow copies.
|
|
968
|
+
const docNeedsCopy = topLevelSect !== doc.sectionProperties || effectiveBody !== doc.body;
|
|
969
|
+
const effectiveDoc = docNeedsCopy
|
|
970
|
+
? {
|
|
971
|
+
...doc,
|
|
972
|
+
...(topLevelSect !== undefined ? { sectionProperties: topLevelSect } : {}),
|
|
973
|
+
body: effectiveBody
|
|
974
|
+
}
|
|
975
|
+
: doc;
|
|
976
|
+
// Collect charts and register relationships BEFORE rendering document.xml.
|
|
977
|
+
// Body charts go into document.xml.rels; charts that live inside a header
|
|
978
|
+
// or footer must be registered against THAT part's own .rels file (they
|
|
979
|
+
// are referenced from header{N}.xml / footer{N}.xml, not document.xml).
|
|
980
|
+
const charts = [];
|
|
981
|
+
collectCharts(doc.body, charts);
|
|
982
|
+
const chartRIds = [];
|
|
983
|
+
const renderCtxChartRIds = new Map();
|
|
984
|
+
charts.forEach(chartContent => {
|
|
985
|
+
const num = chartRIds.length + 1;
|
|
986
|
+
const rId = addRelationship(documentRels, RelType.Chart, `charts/chart${num}.xml`);
|
|
987
|
+
chartRIds.push(rId);
|
|
988
|
+
renderCtxChartRIds.set(chartContent, rId);
|
|
989
|
+
});
|
|
990
|
+
// Register header charts against each header's own .rels.
|
|
991
|
+
if (doc.headers) {
|
|
992
|
+
for (const [key, headerDef] of doc.headers) {
|
|
993
|
+
const headerCharts = [];
|
|
994
|
+
collectChartsFromHeaderFooter(headerDef.content, headerCharts);
|
|
995
|
+
if (headerCharts.length === 0) {
|
|
996
|
+
continue;
|
|
997
|
+
}
|
|
998
|
+
const hRels = headerRelManagers.get(key) ?? createRelationships();
|
|
999
|
+
for (const chartContent of headerCharts) {
|
|
1000
|
+
const num = chartRIds.length + 1;
|
|
1001
|
+
const rId = addRelationship(hRels, RelType.Chart, `charts/chart${num}.xml`);
|
|
1002
|
+
chartRIds.push(rId);
|
|
1003
|
+
renderCtxChartRIds.set(chartContent, rId);
|
|
1004
|
+
// Also push into the global charts[] so chart{num}.xml + .rels are
|
|
1005
|
+
// emitted later (chart parts themselves live under word/charts/, not
|
|
1006
|
+
// co-located with the header).
|
|
1007
|
+
charts.push(chartContent);
|
|
1008
|
+
}
|
|
1009
|
+
headerRelManagers.set(key, hRels);
|
|
1010
|
+
}
|
|
1011
|
+
}
|
|
1012
|
+
// Same for footers.
|
|
1013
|
+
if (doc.footers) {
|
|
1014
|
+
for (const [key, footerDef] of doc.footers) {
|
|
1015
|
+
const footerCharts = [];
|
|
1016
|
+
collectChartsFromHeaderFooter(footerDef.content, footerCharts);
|
|
1017
|
+
if (footerCharts.length === 0) {
|
|
1018
|
+
continue;
|
|
1019
|
+
}
|
|
1020
|
+
const fRels = footerRelManagers.get(key) ?? createRelationships();
|
|
1021
|
+
for (const chartContent of footerCharts) {
|
|
1022
|
+
const num = chartRIds.length + 1;
|
|
1023
|
+
const rId = addRelationship(fRels, RelType.Chart, `charts/chart${num}.xml`);
|
|
1024
|
+
chartRIds.push(rId);
|
|
1025
|
+
renderCtxChartRIds.set(chartContent, rId);
|
|
1026
|
+
charts.push(chartContent);
|
|
1027
|
+
}
|
|
1028
|
+
footerRelManagers.set(key, fRels);
|
|
1029
|
+
}
|
|
1030
|
+
}
|
|
1031
|
+
// Collect ChartEx items and register relationships
|
|
1032
|
+
const chartExItems = [];
|
|
1033
|
+
collectChartExItems(doc.body, chartExItems);
|
|
1034
|
+
const chartExRIds = [];
|
|
1035
|
+
chartExItems.forEach(cxContent => {
|
|
1036
|
+
const num = chartExRIds.length + 1;
|
|
1037
|
+
const rId = addRelationship(documentRels, RelType.ChartEx, `charts/chartEx${num}.xml`);
|
|
1038
|
+
chartExRIds.push(rId);
|
|
1039
|
+
renderCtxChartRIds.set(cxContent, rId);
|
|
1040
|
+
});
|
|
1041
|
+
// Create render context for document serialization. The id generators
|
|
1042
|
+
// are seeded so any auto-assigned id (e.g. SDT) starts above the maximum
|
|
1043
|
+
// value the caller already used in the model — otherwise an SDT without
|
|
1044
|
+
// an explicit `id` would collide with an existing one and break
|
|
1045
|
+
// dataBinding / repeating section linkage.
|
|
1046
|
+
const renderCtx = createRenderContext({
|
|
1047
|
+
securityPolicy,
|
|
1048
|
+
chartRIds: renderCtxChartRIds,
|
|
1049
|
+
imageRIdRemap: imageRemap,
|
|
1050
|
+
hyperlinkRIds,
|
|
1051
|
+
altChunkRIds,
|
|
1052
|
+
ids: createIdGenerators({
|
|
1053
|
+
sdtId: scanMaxSdtId(doc) + 1
|
|
1054
|
+
})
|
|
1055
|
+
});
|
|
1056
|
+
// word/_rels/document.xml.rels — deferred until after all document-level
|
|
1057
|
+
// relationships have been registered (VBA, webSettings, people, charts, etc.)
|
|
1058
|
+
// word/document.xml
|
|
1059
|
+
archive.add(PartPath.Document, renderXml(xml => renderDocument(xml, effectiveDoc, renderCtx)));
|
|
1060
|
+
// word/styles.xml
|
|
1061
|
+
archive.add(PartPath.Styles, renderXml(xml => renderStyles(xml, doc.docDefaults, doc.styles)));
|
|
1062
|
+
// word/settings.xml
|
|
1063
|
+
archive.add(PartPath.Settings, renderXml(xml => renderSettings(xml, doc.settings, rawXmlPolicy)));
|
|
1064
|
+
// word/fontTable.xml
|
|
1065
|
+
archive.add(PartPath.FontTable, renderXml(xml => renderFontTable(xml, doc.fonts)));
|
|
1066
|
+
// word/fonts/*.odttf (embedded fonts)
|
|
1067
|
+
if (doc.embeddedFonts && doc.embeddedFonts.length > 0) {
|
|
1068
|
+
const fontTableRels = createRelationships();
|
|
1069
|
+
const usedRIds = new Set();
|
|
1070
|
+
// Collect rIds referenced in fontTable
|
|
1071
|
+
if (doc.fonts) {
|
|
1072
|
+
for (const font of doc.fonts) {
|
|
1073
|
+
if (font.embedRegular) {
|
|
1074
|
+
usedRIds.add(font.embedRegular);
|
|
1075
|
+
}
|
|
1076
|
+
if (font.embedBold) {
|
|
1077
|
+
usedRIds.add(font.embedBold);
|
|
1078
|
+
}
|
|
1079
|
+
if (font.embedItalic) {
|
|
1080
|
+
usedRIds.add(font.embedItalic);
|
|
1081
|
+
}
|
|
1082
|
+
if (font.embedBoldItalic) {
|
|
1083
|
+
usedRIds.add(font.embedBoldItalic);
|
|
1084
|
+
}
|
|
1085
|
+
}
|
|
1086
|
+
}
|
|
1087
|
+
for (const ef of doc.embeddedFonts) {
|
|
1088
|
+
const partPath = `word/fonts/${ef.fileName}`;
|
|
1089
|
+
// De-dup: two EmbeddedFont entries with the same (rId, fileName) are
|
|
1090
|
+
// the same physical file (typical when callers reuse helpers like
|
|
1091
|
+
// `embedFontFamily` and store the same FontDef across runs). Skip
|
|
1092
|
+
// the duplicate to avoid an `archive.add` overwrite race and a
|
|
1093
|
+
// `Duplicate relationship ID` from `addRelationshipWithId`.
|
|
1094
|
+
if (fontTableRels.hasId(ef.rId)) {
|
|
1095
|
+
continue;
|
|
1096
|
+
}
|
|
1097
|
+
archive.add(partPath, ef.data);
|
|
1098
|
+
// Register relationship from fontTable.xml. We always emit the
|
|
1099
|
+
// relationship (even when no FontDef references it directly) so
|
|
1100
|
+
// the embedded font part doesn't end up orphaned in the package.
|
|
1101
|
+
addRelationshipWithId(fontTableRels, ef.rId, RelType.Font, `fonts/${ef.fileName}`);
|
|
1102
|
+
void usedRIds;
|
|
1103
|
+
// Register content type for .odttf / .ttf
|
|
1104
|
+
const ext = getFileExt(ef.fileName);
|
|
1105
|
+
if (ext === "odttf") {
|
|
1106
|
+
addContentTypeDefault(contentTypes, "odttf", ContentType.ObfuscatedFont);
|
|
1107
|
+
}
|
|
1108
|
+
else if (ext === "ttf") {
|
|
1109
|
+
addContentTypeDefault(contentTypes, "ttf", "application/x-font-ttf");
|
|
1110
|
+
}
|
|
1111
|
+
else if (ext === "otf") {
|
|
1112
|
+
addContentTypeDefault(contentTypes, "otf", "application/x-font-otf");
|
|
1113
|
+
}
|
|
1114
|
+
}
|
|
1115
|
+
// Write fontTable.xml.rels
|
|
1116
|
+
archive.add("word/_rels/fontTable.xml.rels", renderXml(xml => renderRelationships(fontTableRels, xml)));
|
|
1117
|
+
}
|
|
1118
|
+
// Custom XML parts (for SDT data binding)
|
|
1119
|
+
if (doc.customXmlParts && doc.customXmlParts.length > 0) {
|
|
1120
|
+
doc.customXmlParts.forEach((part, i) => {
|
|
1121
|
+
const num = i + 1;
|
|
1122
|
+
const itemPath = `word/customXml/item${num}.xml`;
|
|
1123
|
+
const propsPath = `word/customXml/itemProps${num}.xml`;
|
|
1124
|
+
// Write the XML content
|
|
1125
|
+
archive.add(itemPath, part.xmlContent);
|
|
1126
|
+
// Write itemProps*.xml
|
|
1127
|
+
const propsXml = renderXml(xml => {
|
|
1128
|
+
xml.openXml(STD_DOC_ATTRIBUTES);
|
|
1129
|
+
xml.openNode("ds:datastoreItem", {
|
|
1130
|
+
"ds:itemID": `{${part.itemId}}`,
|
|
1131
|
+
"xmlns:ds": "http://schemas.openxmlformats.org/officeDocument/2006/customXml"
|
|
1132
|
+
});
|
|
1133
|
+
if (part.schemaReferences && part.schemaReferences.length > 0) {
|
|
1134
|
+
xml.openNode("ds:schemaRefs");
|
|
1135
|
+
for (const uri of part.schemaReferences) {
|
|
1136
|
+
xml.leafNode("ds:schemaRef", { "ds:uri": uri });
|
|
1137
|
+
}
|
|
1138
|
+
xml.closeNode();
|
|
1139
|
+
}
|
|
1140
|
+
else {
|
|
1141
|
+
xml.leafNode("ds:schemaRefs");
|
|
1142
|
+
}
|
|
1143
|
+
xml.closeNode();
|
|
1144
|
+
});
|
|
1145
|
+
archive.add(propsPath, propsXml);
|
|
1146
|
+
// Write item rels (links itemN.xml → itemPropsN.xml)
|
|
1147
|
+
const itemRels = createRelationships();
|
|
1148
|
+
addRelationship(itemRels, RelType.CustomXmlProps, `itemProps${num}.xml`);
|
|
1149
|
+
archive.add(`word/customXml/_rels/item${num}.xml.rels`, renderXml(xml => renderRelationships(itemRels, xml)));
|
|
1150
|
+
// Register content types
|
|
1151
|
+
addContentTypeOverride(contentTypes, `/word/customXml/itemProps${num}.xml`, "application/vnd.openxmlformats-officedocument.customXmlProperties+xml");
|
|
1152
|
+
// Add to document rels
|
|
1153
|
+
addRelationship(documentRels, RelType.CustomXml, `customXml/item${num}.xml`);
|
|
1154
|
+
});
|
|
1155
|
+
}
|
|
1156
|
+
// word/theme/theme1.xml
|
|
1157
|
+
archive.add(PartPath.Theme, renderXml(xml => renderTheme(xml, doc.theme, rawXmlPolicy)));
|
|
1158
|
+
// word/numbering.xml
|
|
1159
|
+
if (hasNumbering) {
|
|
1160
|
+
archive.add(PartPath.Numbering, renderXml(xml => renderNumbering(xml, doc.abstractNumberings, doc.numberingInstances, doc.numPicBullets, rawXmlPolicy)));
|
|
1161
|
+
}
|
|
1162
|
+
// word/footnotes.xml + footnotes.xml.rels
|
|
1163
|
+
if (hasFootnotes) {
|
|
1164
|
+
archive.add(PartPath.Footnotes,
|
|
1165
|
+
// Footnotes are an independent OPC part — their r:id values must
|
|
1166
|
+
// resolve against word/_rels/footnotes.xml.rels, not document.xml.rels.
|
|
1167
|
+
renderXml(xml => renderFootnotes(xml, doc.footnotes, { imageRemap: new Map(), hyperlinkRIds, rawXmlPolicy })));
|
|
1168
|
+
if (getRelationshipCount(footnoteRels) > 0) {
|
|
1169
|
+
archive.add("word/_rels/footnotes.xml.rels", renderXml(xml => renderRelationships(footnoteRels, xml)));
|
|
1170
|
+
}
|
|
1171
|
+
}
|
|
1172
|
+
// word/endnotes.xml + endnotes.xml.rels
|
|
1173
|
+
if (hasEndnotes) {
|
|
1174
|
+
archive.add(PartPath.Endnotes, renderXml(xml => renderEndnotes(xml, doc.endnotes, { imageRemap: new Map(), hyperlinkRIds, rawXmlPolicy })));
|
|
1175
|
+
if (getRelationshipCount(endnoteRels) > 0) {
|
|
1176
|
+
archive.add("word/_rels/endnotes.xml.rels", renderXml(xml => renderRelationships(endnoteRels, xml)));
|
|
1177
|
+
}
|
|
1178
|
+
}
|
|
1179
|
+
// word/comments.xml + comments.xml.rels
|
|
1180
|
+
if (hasComments) {
|
|
1181
|
+
archive.add(PartPath.Comments,
|
|
1182
|
+
// Comments live in their own OPC part; pass helpers so embedded
|
|
1183
|
+
// hyperlinks/images render with the right r:id (the rels manager
|
|
1184
|
+
// below registered them under their model-original id).
|
|
1185
|
+
renderXml(xml => renderComments(xml, doc.comments, { imageRemap: new Map(), hyperlinkRIds, rawXmlPolicy })));
|
|
1186
|
+
if (getRelationshipCount(commentRels) > 0) {
|
|
1187
|
+
archive.add("word/_rels/comments.xml.rels", renderXml(xml => renderRelationships(commentRels, xml)));
|
|
1188
|
+
}
|
|
1189
|
+
// word/commentsExtended.xml (for done/parentId)
|
|
1190
|
+
const hasExtended = doc.comments.some(c => c.done !== undefined || c.parentId !== undefined);
|
|
1191
|
+
if (hasExtended) {
|
|
1192
|
+
const extXml = renderXml(xml => renderCommentsExtended(xml, doc.comments));
|
|
1193
|
+
archive.add(PartPath.CommentsExtended, extXml);
|
|
1194
|
+
addRelationship(documentRels, RelType.CommentsExtended, "commentsExtended.xml");
|
|
1195
|
+
addContentTypeOverride(contentTypes, `/${PartPath.CommentsExtended}`, ContentType.CommentsExtended);
|
|
1196
|
+
}
|
|
1197
|
+
}
|
|
1198
|
+
// Headers
|
|
1199
|
+
headerIndex = 1;
|
|
1200
|
+
if (doc.headers) {
|
|
1201
|
+
let hIdx = 0;
|
|
1202
|
+
const keys = [...doc.headers.keys()];
|
|
1203
|
+
for (const [, headerDef] of doc.headers) {
|
|
1204
|
+
archive.add(PartPath.header(headerIndex),
|
|
1205
|
+
// Header .rels is independent from document .rels — pass an empty
|
|
1206
|
+
// imageRemap so we never rewrite r:embed against the document remap.
|
|
1207
|
+
renderXml(xml => renderHeader(xml, headerDef.content, {
|
|
1208
|
+
imageRemap: new Map(),
|
|
1209
|
+
hyperlinkRIds,
|
|
1210
|
+
rawXmlPolicy
|
|
1211
|
+
})));
|
|
1212
|
+
// Header .rels file
|
|
1213
|
+
const hKey = keys[hIdx];
|
|
1214
|
+
const hRels = headerRelManagers.get(hKey);
|
|
1215
|
+
if (hRels && getRelationshipCount(hRels) > 0) {
|
|
1216
|
+
archive.add(`word/_rels/header${headerIndex}.xml.rels`, renderXml(xml => renderRelationships(hRels, xml)));
|
|
1217
|
+
}
|
|
1218
|
+
headerIndex++;
|
|
1219
|
+
hIdx++;
|
|
1220
|
+
}
|
|
1221
|
+
}
|
|
1222
|
+
// Footers
|
|
1223
|
+
footerIndex = 1;
|
|
1224
|
+
if (doc.footers) {
|
|
1225
|
+
let fIdx = 0;
|
|
1226
|
+
const keys = [...doc.footers.keys()];
|
|
1227
|
+
for (const [, footerDef] of doc.footers) {
|
|
1228
|
+
archive.add(PartPath.footer(footerIndex),
|
|
1229
|
+
// Footer .rels is independent from document .rels.
|
|
1230
|
+
renderXml(xml => renderFooter(xml, footerDef.content, {
|
|
1231
|
+
imageRemap: new Map(),
|
|
1232
|
+
hyperlinkRIds,
|
|
1233
|
+
rawXmlPolicy
|
|
1234
|
+
})));
|
|
1235
|
+
// Footer .rels file
|
|
1236
|
+
const fKey = keys[fIdx];
|
|
1237
|
+
const fRels = footerRelManagers.get(fKey);
|
|
1238
|
+
if (fRels && getRelationshipCount(fRels) > 0) {
|
|
1239
|
+
archive.add(`word/_rels/footer${footerIndex}.xml.rels`, renderXml(xml => renderRelationships(fRels, xml)));
|
|
1240
|
+
}
|
|
1241
|
+
footerIndex++;
|
|
1242
|
+
fIdx++;
|
|
1243
|
+
}
|
|
1244
|
+
}
|
|
1245
|
+
// Watermark header
|
|
1246
|
+
if (watermarkHeaderIndex !== undefined && doc.watermark) {
|
|
1247
|
+
// For image watermarks the model rId may have been remapped onto a
|
|
1248
|
+
// canonical `rId<N>` form when registering the watermark .rels (so
|
|
1249
|
+
// Word accepts the package). Pass the remapped value through so the
|
|
1250
|
+
// VML shape's r:id attribute matches the .rels entry.
|
|
1251
|
+
const watermarkImageRId = doc.watermark.type === "image"
|
|
1252
|
+
? (imageRemap.get(doc.watermark.rId) ?? doc.watermark.rId)
|
|
1253
|
+
: undefined;
|
|
1254
|
+
archive.add(PartPath.header(watermarkHeaderIndex), renderXml(xml => renderWatermarkHeader(xml, doc.watermark, watermarkImageRId)));
|
|
1255
|
+
if (watermarkHeaderRels && getRelationshipCount(watermarkHeaderRels) > 0) {
|
|
1256
|
+
archive.add(`word/_rels/header${watermarkHeaderIndex}.xml.rels`, renderXml(xml => renderRelationships(watermarkHeaderRels, xml)));
|
|
1257
|
+
}
|
|
1258
|
+
}
|
|
1259
|
+
// Media / images
|
|
1260
|
+
if (doc.images) {
|
|
1261
|
+
for (const img of doc.images) {
|
|
1262
|
+
archive.add(PartPath.media(img.fileName), img.data);
|
|
1263
|
+
}
|
|
1264
|
+
}
|
|
1265
|
+
// SVG fallback PNG files
|
|
1266
|
+
for (const fb of svgFallbacks) {
|
|
1267
|
+
archive.add(PartPath.media(fb.fallbackFileName), fb.data);
|
|
1268
|
+
}
|
|
1269
|
+
// docProps/core.xml
|
|
1270
|
+
archive.add(PartPath.CoreProps, renderXml(xml => renderCoreProperties(xml, doc.coreProperties)));
|
|
1271
|
+
// docProps/app.xml
|
|
1272
|
+
archive.add(PartPath.AppProps, renderXml(xml => renderAppProperties(xml, doc.appProperties)));
|
|
1273
|
+
// docProps/custom.xml
|
|
1274
|
+
if (hasCustomProps) {
|
|
1275
|
+
archive.add(PartPath.CustomProps, renderXml(xml => renderCustomProperties(xml, doc.customProperties)));
|
|
1276
|
+
}
|
|
1277
|
+
// word/webSettings.xml
|
|
1278
|
+
if (doc.webSettings) {
|
|
1279
|
+
archive.add(PartPath.WebSettings, renderXml(xml => renderWebSettings(xml, doc.webSettings, rawXmlPolicy)));
|
|
1280
|
+
addRelationship(documentRels, RelType.WebSettings, "webSettings.xml");
|
|
1281
|
+
addContentTypeOverride(contentTypes, `/${PartPath.WebSettings}`, ContentType.WebSettings);
|
|
1282
|
+
}
|
|
1283
|
+
// word/people.xml
|
|
1284
|
+
if (doc.people && doc.people.length > 0) {
|
|
1285
|
+
archive.add(PartPath.People, renderXml(xml => renderPeople(xml, doc.people)));
|
|
1286
|
+
addRelationship(documentRels, RelType.People, "people.xml");
|
|
1287
|
+
addContentTypeOverride(contentTypes, `/${PartPath.People}`, ContentType.People);
|
|
1288
|
+
}
|
|
1289
|
+
// docProps/thumbnail
|
|
1290
|
+
if (doc.thumbnail) {
|
|
1291
|
+
const ext = doc.thumbnail.contentType === "image/jpeg"
|
|
1292
|
+
? "jpeg"
|
|
1293
|
+
: doc.thumbnail.contentType === "image/png"
|
|
1294
|
+
? "png"
|
|
1295
|
+
: "wmf";
|
|
1296
|
+
const thumbPath = `docProps/thumbnail.${ext}`;
|
|
1297
|
+
archive.add(thumbPath, doc.thumbnail.data);
|
|
1298
|
+
// Package rels: target is relative to package root (docProps/thumbnail.jpeg)
|
|
1299
|
+
addRelationship(packageRels, "http://schemas.openxmlformats.org/package/2006/relationships/metadata/thumbnail", thumbPath);
|
|
1300
|
+
addContentTypeDefault(contentTypes, ext, doc.thumbnail.contentType);
|
|
1301
|
+
}
|
|
1302
|
+
// word/vbaProject.bin (macro-enabled documents)
|
|
1303
|
+
if (doc.vbaProject) {
|
|
1304
|
+
archive.add("word/vbaProject.bin", doc.vbaProject);
|
|
1305
|
+
addRelationship(documentRels, RelType.VbaProject, "vbaProject.bin");
|
|
1306
|
+
addContentTypeOverride(contentTypes, "/word/vbaProject.bin", ContentType.VbaProject);
|
|
1307
|
+
}
|
|
1308
|
+
// Write opaque (unrecognized) parts for round-trip preservation.
|
|
1309
|
+
//
|
|
1310
|
+
// Opaque parts are written as-is into the ZIP. Their paths must not
|
|
1311
|
+
// collide with parts the packager itself produces — otherwise we either
|
|
1312
|
+
// emit a duplicate ZIP entry (Word may pick whichever it sees first, but
|
|
1313
|
+
// the produced package is no longer well-formed) or we silently overwrite
|
|
1314
|
+
// the semantic part. Round-trip via `readDocx` already excludes parts
|
|
1315
|
+
// that were consumed; collisions in practice come from callers that
|
|
1316
|
+
// construct opaqueParts by hand. Reject the most dangerous overlaps with
|
|
1317
|
+
// a clear error.
|
|
1318
|
+
if (doc.opaqueParts) {
|
|
1319
|
+
// Exact-path reservations: any well-known core part the packager always
|
|
1320
|
+
// emits, plus the parts we have already added during this run (headers,
|
|
1321
|
+
// footers, charts, embedded fonts, etc).
|
|
1322
|
+
const reservedExact = new Set([
|
|
1323
|
+
PartPath.ContentTypes,
|
|
1324
|
+
PartPath.PackageRels,
|
|
1325
|
+
PartPath.Document,
|
|
1326
|
+
PartPath.DocumentRels,
|
|
1327
|
+
PartPath.Styles,
|
|
1328
|
+
"word/_rels/styles.xml.rels",
|
|
1329
|
+
PartPath.Settings,
|
|
1330
|
+
PartPath.FontTable,
|
|
1331
|
+
"word/_rels/fontTable.xml.rels",
|
|
1332
|
+
PartPath.Numbering,
|
|
1333
|
+
PartPath.Footnotes,
|
|
1334
|
+
"word/_rels/footnotes.xml.rels",
|
|
1335
|
+
PartPath.Endnotes,
|
|
1336
|
+
"word/_rels/endnotes.xml.rels",
|
|
1337
|
+
PartPath.Comments,
|
|
1338
|
+
"word/_rels/comments.xml.rels",
|
|
1339
|
+
PartPath.CommentsExtended,
|
|
1340
|
+
PartPath.People,
|
|
1341
|
+
PartPath.WebSettings,
|
|
1342
|
+
PartPath.Theme,
|
|
1343
|
+
PartPath.CoreProps,
|
|
1344
|
+
PartPath.AppProps,
|
|
1345
|
+
PartPath.CustomProps,
|
|
1346
|
+
PartPath.Thumbnail,
|
|
1347
|
+
"word/vbaProject.bin",
|
|
1348
|
+
"word/_rels/vbaProject.bin.rels"
|
|
1349
|
+
]);
|
|
1350
|
+
// Headers/footers we are emitting in this run.
|
|
1351
|
+
if (doc.headers) {
|
|
1352
|
+
let i = 1;
|
|
1353
|
+
for (const _entry of doc.headers) {
|
|
1354
|
+
void _entry;
|
|
1355
|
+
reservedExact.add(PartPath.header(i));
|
|
1356
|
+
reservedExact.add(PartPath.headerRels(i));
|
|
1357
|
+
i++;
|
|
1358
|
+
}
|
|
1359
|
+
}
|
|
1360
|
+
if (doc.footers) {
|
|
1361
|
+
let i = 1;
|
|
1362
|
+
for (const _entry of doc.footers) {
|
|
1363
|
+
void _entry;
|
|
1364
|
+
reservedExact.add(PartPath.footer(i));
|
|
1365
|
+
reservedExact.add(PartPath.footerRels(i));
|
|
1366
|
+
i++;
|
|
1367
|
+
}
|
|
1368
|
+
}
|
|
1369
|
+
// Charts emitted by this run.
|
|
1370
|
+
for (let i = 1; i <= charts.length; i++) {
|
|
1371
|
+
reservedExact.add(`word/charts/chart${i}.xml`);
|
|
1372
|
+
reservedExact.add(`word/charts/_rels/chart${i}.xml.rels`);
|
|
1373
|
+
}
|
|
1374
|
+
for (let i = 1; i <= chartExItems.length; i++) {
|
|
1375
|
+
reservedExact.add(`word/charts/chartEx${i}.xml`);
|
|
1376
|
+
}
|
|
1377
|
+
// Image media: each image has a deterministic media path.
|
|
1378
|
+
if (doc.images) {
|
|
1379
|
+
for (const img of doc.images) {
|
|
1380
|
+
if (img.fileName) {
|
|
1381
|
+
reservedExact.add(`word/media/${img.fileName}`);
|
|
1382
|
+
}
|
|
1383
|
+
}
|
|
1384
|
+
}
|
|
1385
|
+
// Embedded fonts.
|
|
1386
|
+
if (doc.embeddedFonts) {
|
|
1387
|
+
for (const ef of doc.embeddedFonts) {
|
|
1388
|
+
if (ef.fileName) {
|
|
1389
|
+
reservedExact.add(`word/fonts/${ef.fileName}`);
|
|
1390
|
+
}
|
|
1391
|
+
}
|
|
1392
|
+
}
|
|
1393
|
+
for (const part of doc.opaqueParts) {
|
|
1394
|
+
if (reservedExact.has(part.path)) {
|
|
1395
|
+
throw new DocxWriteError(`Opaque part path "${part.path}" conflicts with a part the packager ` +
|
|
1396
|
+
`is emitting itself. Move the data into the corresponding model ` +
|
|
1397
|
+
`field (e.g. doc.styles, doc.headers, doc.images) or rename the ` +
|
|
1398
|
+
`opaque part.`);
|
|
1399
|
+
}
|
|
1400
|
+
// Honour `preserveOleObjects`: drop OLE binaries before they reach
|
|
1401
|
+
// the ZIP. The relationships that referenced them remain in the
|
|
1402
|
+
// owning part's .rels, but no payload will be written.
|
|
1403
|
+
if (!securityPolicy.preserveOleObjects &&
|
|
1404
|
+
(part.path.startsWith("word/embeddings/") ||
|
|
1405
|
+
(part.path.endsWith(".bin") && part.path.includes("embed")))) {
|
|
1406
|
+
continue;
|
|
1407
|
+
}
|
|
1408
|
+
// Honour `dropSignaturesOnModify`: any digital-signature part lives
|
|
1409
|
+
// under `_xmlsignatures/`. Once the document has been re-serialised
|
|
1410
|
+
// the signature would no longer match the new bytes, so by default
|
|
1411
|
+
// we drop these parts entirely. The corresponding sigOrigin
|
|
1412
|
+
// relationship from `_rels/.rels` is also gone — packageRels is
|
|
1413
|
+
// rebuilt from scratch, so the dangling reference disappears too.
|
|
1414
|
+
if (securityPolicy.dropSignaturesOnModify && part.path.startsWith("_xmlsignatures/")) {
|
|
1415
|
+
continue;
|
|
1416
|
+
}
|
|
1417
|
+
archive.add(part.path, part.data);
|
|
1418
|
+
// Register content type: explicit > inferred by extension > skip
|
|
1419
|
+
const ext = getFileExt(part.path);
|
|
1420
|
+
if (part.contentType) {
|
|
1421
|
+
addContentTypeOverride(contentTypes, `/${part.path}`, part.contentType);
|
|
1422
|
+
}
|
|
1423
|
+
else if (ext) {
|
|
1424
|
+
// Infer from common extensions so [Content_Types].xml isn't incomplete
|
|
1425
|
+
const inferred = inferContentType(ext);
|
|
1426
|
+
if (inferred) {
|
|
1427
|
+
addContentTypeOverride(contentTypes, `/${part.path}`, inferred);
|
|
1428
|
+
}
|
|
1429
|
+
}
|
|
1430
|
+
// Write part relationships if any
|
|
1431
|
+
if (part.relationships && part.relationships.length > 0) {
|
|
1432
|
+
const partRels = createRelationships();
|
|
1433
|
+
for (const rel of part.relationships) {
|
|
1434
|
+
addRelationshipWithId(partRels, rel.id, rel.type, rel.target, rel.targetMode);
|
|
1435
|
+
}
|
|
1436
|
+
const relsPath = getPartRelsPath(part.path);
|
|
1437
|
+
archive.add(relsPath, renderXml(xml => renderRelationships(partRels, xml)));
|
|
1438
|
+
}
|
|
1439
|
+
}
|
|
1440
|
+
}
|
|
1441
|
+
// Write chart parts (rIds already registered earlier)
|
|
1442
|
+
const chartEmbedPromises = [];
|
|
1443
|
+
charts.forEach((chartContent, i) => {
|
|
1444
|
+
const num = i + 1;
|
|
1445
|
+
const chartPath = `word/charts/chart${num}.xml`;
|
|
1446
|
+
// Register chart part
|
|
1447
|
+
archive.add(chartPath, renderXml(xml => renderChartPart(xml, chartContent.chart)));
|
|
1448
|
+
// Register content type
|
|
1449
|
+
addContentTypeOverride(contentTypes, `/word/charts/chart${num}.xml`, ContentType.Chart);
|
|
1450
|
+
// Embed xlsx workbook if requested
|
|
1451
|
+
if (chartContent.chart.embedWorkbook) {
|
|
1452
|
+
const xlsxPath = `word/embeddings/Microsoft_Excel_Worksheet${num}.xlsx`;
|
|
1453
|
+
// Create chart .rels file referencing the embedded xlsx
|
|
1454
|
+
const chartRels = createRelationships();
|
|
1455
|
+
addRelationship(chartRels, RelType.Package, `../embeddings/Microsoft_Excel_Worksheet${num}.xlsx`);
|
|
1456
|
+
archive.add(`word/charts/_rels/chart${num}.xml.rels`, renderXml(xml => renderRelationships(chartRels, xml)));
|
|
1457
|
+
// Register xlsx content type
|
|
1458
|
+
addContentTypeOverride(contentTypes, `/${xlsxPath}`, ContentType.Xlsx);
|
|
1459
|
+
// Generate and add xlsx asynchronously via Excel module (dynamic import)
|
|
1460
|
+
const promise = import("../bridge/excel-bridge.js").then(async ({ generateChartEmbeddedXlsx }) => {
|
|
1461
|
+
const xlsxData = await generateChartEmbeddedXlsx(chartContent.chart.series);
|
|
1462
|
+
archive.add(xlsxPath, xlsxData);
|
|
1463
|
+
});
|
|
1464
|
+
chartEmbedPromises.push(promise);
|
|
1465
|
+
}
|
|
1466
|
+
});
|
|
1467
|
+
// Wait for all embedded xlsx files to be generated
|
|
1468
|
+
if (chartEmbedPromises.length > 0) {
|
|
1469
|
+
await Promise.all(chartEmbedPromises);
|
|
1470
|
+
}
|
|
1471
|
+
// Write ChartEx parts (cx:chartSpace XML)
|
|
1472
|
+
chartExItems.forEach((cxContent, i) => {
|
|
1473
|
+
const num = i + 1;
|
|
1474
|
+
const cxPath = `word/charts/chartEx${num}.xml`;
|
|
1475
|
+
archive.add(cxPath, cxContent.chartExXml);
|
|
1476
|
+
addContentTypeOverride(contentTypes, `/${cxPath}`, ContentType.ChartEx);
|
|
1477
|
+
});
|
|
1478
|
+
// LAST: Write [Content_Types].xml, _rels/.rels, and word/_rels/document.xml.rels
|
|
1479
|
+
// after all parts have registered their content types and relationships.
|
|
1480
|
+
// Validate relationships before serializing (catch duplicate IDs, missing TargetMode, etc.)
|
|
1481
|
+
const docRelErrors = documentRels.validate();
|
|
1482
|
+
const pkgRelErrors = packageRels.validate();
|
|
1483
|
+
if (docRelErrors.length > 0 || pkgRelErrors.length > 0) {
|
|
1484
|
+
const allErrors = [...pkgRelErrors, ...docRelErrors];
|
|
1485
|
+
throw new DocxWriteError(`OPC relationship validation failed:\n${allErrors.join("\n")}`);
|
|
1486
|
+
}
|
|
1487
|
+
archive.add(PartPath.DocumentRels, renderXml(xml => renderRelationships(documentRels, xml)));
|
|
1488
|
+
archive.add(PartPath.ContentTypes, renderXml(xml => renderContentTypes(contentTypes, xml)));
|
|
1489
|
+
archive.add(PartPath.PackageRels, renderXml(xml => renderRelationships(packageRels, xml)));
|
|
1490
|
+
return archive.bytes();
|
|
1491
|
+
}
|
|
1492
|
+
/** Recursively collect altChunks from body content. */
|
|
1493
|
+
function collectAltChunks(body, out) {
|
|
1494
|
+
walkBlocks(body, {
|
|
1495
|
+
visitAltChunk(chunk) {
|
|
1496
|
+
out.push(chunk);
|
|
1497
|
+
}
|
|
1498
|
+
});
|
|
1499
|
+
}
|
|
1500
|
+
/** Recursively collect chart contents from body content. */
|
|
1501
|
+
function collectCharts(body, out) {
|
|
1502
|
+
walkBlocks(body, {
|
|
1503
|
+
visitChart(chart) {
|
|
1504
|
+
out.push(chart);
|
|
1505
|
+
}
|
|
1506
|
+
});
|
|
1507
|
+
}
|
|
1508
|
+
/** Recursively collect ChartEx contents from body content. */
|
|
1509
|
+
function collectChartExItems(body, out) {
|
|
1510
|
+
walkBlocks(body, {
|
|
1511
|
+
visitChartEx(chart) {
|
|
1512
|
+
out.push(chart);
|
|
1513
|
+
}
|
|
1514
|
+
});
|
|
1515
|
+
}
|