@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,1435 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DOCX Module - Template Engine
|
|
3
|
+
*
|
|
4
|
+
* A template engine for DOCX documents supporting:
|
|
5
|
+
* - Variable interpolation: {{name}}, {{user.name}}
|
|
6
|
+
* - Conditionals: {{#if cond}}...{{/if}}, {{#if cond}}...{{else}}...{{/if}}
|
|
7
|
+
* - Loops: {{#each items}}...{{/each}} with {{.}}, {{.prop}}, {{@index}}
|
|
8
|
+
* - Table row loops: auto-duplicates table rows when {{#each}} is in a row
|
|
9
|
+
* - Table column loops: {{#cols items}}...{{/cols}} duplicates columns
|
|
10
|
+
* - Image placeholders: {{%image}} resolves to an inline image
|
|
11
|
+
* - RichText placeholders: {{&richText}} resolves to paragraph children
|
|
12
|
+
* - Sub-document insertion: {{>subDoc}} resolves to body content blocks
|
|
13
|
+
* - Chart placeholders: {{^chart}} resolves to chart content
|
|
14
|
+
*
|
|
15
|
+
* Handles cross-run placeholders (Word often splits {{name}} across runs).
|
|
16
|
+
*/
|
|
17
|
+
import { utf8Encoder } from "../core/internal-utils.js";
|
|
18
|
+
import { isRun } from "../core/text-utils.js";
|
|
19
|
+
import { DocxError } from "../errors.js";
|
|
20
|
+
/** Error thrown when template processing fails. */
|
|
21
|
+
export class TemplateError extends DocxError {
|
|
22
|
+
constructor(message, placeholder, location, options) {
|
|
23
|
+
super(message, options);
|
|
24
|
+
this.name = "TemplateError";
|
|
25
|
+
this.placeholder = placeholder;
|
|
26
|
+
this.location = location;
|
|
27
|
+
this.tagName = options?.tagName;
|
|
28
|
+
this.paragraphIndex = options?.paragraphIndex;
|
|
29
|
+
this.sectionPath = options?.sectionPath;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
// =============================================================================
|
|
33
|
+
// Token Types (internal)
|
|
34
|
+
// =============================================================================
|
|
35
|
+
var TokenType;
|
|
36
|
+
(function (TokenType) {
|
|
37
|
+
TokenType[TokenType["Text"] = 0] = "Text";
|
|
38
|
+
TokenType[TokenType["Variable"] = 1] = "Variable";
|
|
39
|
+
TokenType[TokenType["IfOpen"] = 2] = "IfOpen";
|
|
40
|
+
TokenType[TokenType["Else"] = 3] = "Else";
|
|
41
|
+
TokenType[TokenType["IfClose"] = 4] = "IfClose";
|
|
42
|
+
TokenType[TokenType["EachOpen"] = 5] = "EachOpen";
|
|
43
|
+
TokenType[TokenType["EachClose"] = 6] = "EachClose";
|
|
44
|
+
})(TokenType || (TokenType = {}));
|
|
45
|
+
// =============================================================================
|
|
46
|
+
// Core: fillTemplate
|
|
47
|
+
// =============================================================================
|
|
48
|
+
/**
|
|
49
|
+
* Fill a DOCX template document with data.
|
|
50
|
+
*
|
|
51
|
+
* Processes all body content, headers, footers, footnotes, and endnotes.
|
|
52
|
+
* Operates on the document model in-place and returns it.
|
|
53
|
+
*
|
|
54
|
+
* @param doc - The parsed DocxDocument model.
|
|
55
|
+
* @param data - Data to fill into the template.
|
|
56
|
+
* @param options - Optional template settings.
|
|
57
|
+
* @returns The same DocxDocument with placeholders resolved.
|
|
58
|
+
*/
|
|
59
|
+
export function fillTemplate(doc, data, options) {
|
|
60
|
+
const open = options?.delimiters?.[0] ?? "{{";
|
|
61
|
+
const close = options?.delimiters?.[1] ?? "}}";
|
|
62
|
+
const strict = options?.strict ?? true;
|
|
63
|
+
const ctx = { open, close, strict, data };
|
|
64
|
+
// Process body
|
|
65
|
+
const newBody = processBodyContent(doc.body, ctx, "body");
|
|
66
|
+
doc.body = newBody;
|
|
67
|
+
// Process headers
|
|
68
|
+
if (doc.headers) {
|
|
69
|
+
for (const [key, headerDef] of doc.headers) {
|
|
70
|
+
const newChildren = processBlockList(headerDef.content.children, ctx, `header:${key}`);
|
|
71
|
+
headerDef.content.children = newChildren;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
// Process footers
|
|
75
|
+
if (doc.footers) {
|
|
76
|
+
for (const [key, footerDef] of doc.footers) {
|
|
77
|
+
const newChildren = processBlockList(footerDef.content.children, ctx, `footer:${key}`);
|
|
78
|
+
footerDef.content.children = newChildren;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
// Process footnotes
|
|
82
|
+
if (doc.footnotes) {
|
|
83
|
+
for (const note of doc.footnotes) {
|
|
84
|
+
if (note.id <= 0) {
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
87
|
+
const processed = processBlockList(note.content, ctx, `footnote:${note.id}`);
|
|
88
|
+
note.content = processed;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
// Process endnotes
|
|
92
|
+
if (doc.endnotes) {
|
|
93
|
+
for (const note of doc.endnotes) {
|
|
94
|
+
if (note.id <= 0) {
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
const processed = processBlockList(note.content, ctx, `endnote:${note.id}`);
|
|
98
|
+
note.content = processed;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return doc;
|
|
102
|
+
}
|
|
103
|
+
// =============================================================================
|
|
104
|
+
// Cross-run text merging
|
|
105
|
+
// =============================================================================
|
|
106
|
+
/**
|
|
107
|
+
* Merge all text content from a paragraph's runs into a single string.
|
|
108
|
+
* This is intentionally simple — only extracts raw text from runs (no hyperlinks,
|
|
109
|
+
* no tracked changes) as template placeholders only exist in direct run content.
|
|
110
|
+
*/
|
|
111
|
+
function extractParagraphText(para) {
|
|
112
|
+
let result = "";
|
|
113
|
+
for (const child of para.children) {
|
|
114
|
+
if (!isRun(child)) {
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
117
|
+
for (const c of child.content) {
|
|
118
|
+
if (c.type === "text" && "text" in c) {
|
|
119
|
+
result += c.text;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
return result;
|
|
124
|
+
}
|
|
125
|
+
// =============================================================================
|
|
126
|
+
// Tokenizer
|
|
127
|
+
// =============================================================================
|
|
128
|
+
function tokenize(text, open, close) {
|
|
129
|
+
const tokens = [];
|
|
130
|
+
let pos = 0;
|
|
131
|
+
while (pos < text.length) {
|
|
132
|
+
const startIdx = text.indexOf(open, pos);
|
|
133
|
+
if (startIdx === -1) {
|
|
134
|
+
tokens.push({ type: TokenType.Text, value: text.slice(pos) });
|
|
135
|
+
break;
|
|
136
|
+
}
|
|
137
|
+
if (startIdx > pos) {
|
|
138
|
+
tokens.push({ type: TokenType.Text, value: text.slice(pos, startIdx) });
|
|
139
|
+
}
|
|
140
|
+
const endIdx = text.indexOf(close, startIdx + open.length);
|
|
141
|
+
if (endIdx === -1) {
|
|
142
|
+
// Unclosed delimiter — treat rest as text
|
|
143
|
+
tokens.push({ type: TokenType.Text, value: text.slice(startIdx) });
|
|
144
|
+
break;
|
|
145
|
+
}
|
|
146
|
+
const expr = text.slice(startIdx + open.length, endIdx).trim();
|
|
147
|
+
pos = endIdx + close.length;
|
|
148
|
+
if (expr.startsWith("#if ")) {
|
|
149
|
+
tokens.push({ type: TokenType.IfOpen, condition: expr.slice(4).trim() });
|
|
150
|
+
}
|
|
151
|
+
else if (expr === "else") {
|
|
152
|
+
tokens.push({ type: TokenType.Else });
|
|
153
|
+
}
|
|
154
|
+
else if (expr === "/if") {
|
|
155
|
+
tokens.push({ type: TokenType.IfClose });
|
|
156
|
+
}
|
|
157
|
+
else if (expr.startsWith("#each ")) {
|
|
158
|
+
tokens.push({ type: TokenType.EachOpen, collection: expr.slice(6).trim() });
|
|
159
|
+
}
|
|
160
|
+
else if (expr === "/each") {
|
|
161
|
+
tokens.push({ type: TokenType.EachClose });
|
|
162
|
+
}
|
|
163
|
+
else {
|
|
164
|
+
tokens.push({ type: TokenType.Variable, path: expr });
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
return tokens;
|
|
168
|
+
}
|
|
169
|
+
// =============================================================================
|
|
170
|
+
// Value resolution
|
|
171
|
+
// =============================================================================
|
|
172
|
+
function resolvePath(data, path) {
|
|
173
|
+
if (path === ".") {
|
|
174
|
+
// In loop context, "." refers to the current item stored at key "."
|
|
175
|
+
if (data != null && typeof data === "object" && Object.hasOwn(data, ".")) {
|
|
176
|
+
return data["."];
|
|
177
|
+
}
|
|
178
|
+
return data;
|
|
179
|
+
}
|
|
180
|
+
const startPath = path.startsWith(".") ? path.slice(1) : path;
|
|
181
|
+
const parts = startPath.split(".");
|
|
182
|
+
let current = data;
|
|
183
|
+
for (const part of parts) {
|
|
184
|
+
if (current == null || typeof current !== "object") {
|
|
185
|
+
return undefined;
|
|
186
|
+
}
|
|
187
|
+
// Use Object.hasOwn so a template path can never traverse prototype
|
|
188
|
+
// properties (`{{constructor}}`, `{{__proto__.something}}`, …) and
|
|
189
|
+
// surface JS internals into the rendered document. Arrays are
|
|
190
|
+
// handled the same way — numeric indices are own properties.
|
|
191
|
+
if (!Object.hasOwn(current, part)) {
|
|
192
|
+
return undefined;
|
|
193
|
+
}
|
|
194
|
+
current = current[part];
|
|
195
|
+
}
|
|
196
|
+
return current;
|
|
197
|
+
}
|
|
198
|
+
function isTruthy(value) {
|
|
199
|
+
if (Array.isArray(value)) {
|
|
200
|
+
return value.length > 0;
|
|
201
|
+
}
|
|
202
|
+
return !!value;
|
|
203
|
+
}
|
|
204
|
+
function valueToString(value) {
|
|
205
|
+
if (value == null) {
|
|
206
|
+
return "";
|
|
207
|
+
}
|
|
208
|
+
if (typeof value === "object") {
|
|
209
|
+
return JSON.stringify(value);
|
|
210
|
+
}
|
|
211
|
+
return String(value);
|
|
212
|
+
}
|
|
213
|
+
// =============================================================================
|
|
214
|
+
// Processing body content (handles block-level #each and #if)
|
|
215
|
+
// =============================================================================
|
|
216
|
+
function processBodyContent(blocks, ctx, location) {
|
|
217
|
+
const result = [];
|
|
218
|
+
let i = 0;
|
|
219
|
+
while (i < blocks.length) {
|
|
220
|
+
const block = blocks[i];
|
|
221
|
+
if (block.type === "paragraph") {
|
|
222
|
+
const text = extractParagraphText(block);
|
|
223
|
+
// Check if this paragraph starts a block-level #each
|
|
224
|
+
if (hasBlockDirective(text, ctx.open, ctx.close, "#each ")) {
|
|
225
|
+
const collectionExpr = extractDirectiveArg(text, ctx.open, ctx.close, "#each ");
|
|
226
|
+
const endIdx = findClosingBlock(blocks, i, ctx, "#each ", "/each");
|
|
227
|
+
if (endIdx === -1) {
|
|
228
|
+
throw new TemplateError(`Unclosed {{#each ${collectionExpr}}}`, `#each ${collectionExpr}`, location, {
|
|
229
|
+
tagName: `#each ${collectionExpr}`,
|
|
230
|
+
paragraphIndex: i,
|
|
231
|
+
sectionPath: location
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
const innerBlocks = blocks.slice(i + 1, endIdx);
|
|
235
|
+
const items = resolvePath(ctx.data, collectionExpr);
|
|
236
|
+
if (Array.isArray(items)) {
|
|
237
|
+
for (let idx = 0; idx < items.length; idx++) {
|
|
238
|
+
const itemData = buildLoopData(items[idx], idx, ctx.data);
|
|
239
|
+
const innerCtx = { ...ctx, data: itemData };
|
|
240
|
+
const processed = processBodyContent(cloneBlocks(innerBlocks), innerCtx, `${location}[${idx}]`);
|
|
241
|
+
result.push(...processed);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
else if (ctx.strict && items !== undefined && items !== null) {
|
|
245
|
+
throw new TemplateError(`{{#each ${collectionExpr}}} expects an array, got ${typeof items}`, `#each ${collectionExpr}`, location, {
|
|
246
|
+
tagName: `#each ${collectionExpr}`,
|
|
247
|
+
paragraphIndex: i,
|
|
248
|
+
sectionPath: location
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
i = endIdx + 1;
|
|
252
|
+
continue;
|
|
253
|
+
}
|
|
254
|
+
// Check if this paragraph starts a block-level #if
|
|
255
|
+
if (hasBlockDirective(text, ctx.open, ctx.close, "#if ")) {
|
|
256
|
+
const condExpr = extractDirectiveArg(text, ctx.open, ctx.close, "#if ");
|
|
257
|
+
const { elseIdx, endIdx } = findIfBlock(blocks, i, ctx);
|
|
258
|
+
if (endIdx === -1) {
|
|
259
|
+
throw new TemplateError(`Unclosed {{#if ${condExpr}}}`, `#if ${condExpr}`, location, {
|
|
260
|
+
tagName: `#if ${condExpr}`,
|
|
261
|
+
paragraphIndex: i,
|
|
262
|
+
sectionPath: location
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
const condValue = resolvePath(ctx.data, condExpr);
|
|
266
|
+
let innerBlocks;
|
|
267
|
+
if (isTruthy(condValue)) {
|
|
268
|
+
innerBlocks = blocks.slice(i + 1, elseIdx !== -1 ? elseIdx : endIdx);
|
|
269
|
+
}
|
|
270
|
+
else {
|
|
271
|
+
innerBlocks = elseIdx !== -1 ? blocks.slice(elseIdx + 1, endIdx) : [];
|
|
272
|
+
}
|
|
273
|
+
const processed = processBodyContent(cloneBlocks(innerBlocks), ctx, location);
|
|
274
|
+
result.push(...processed);
|
|
275
|
+
i = endIdx + 1;
|
|
276
|
+
continue;
|
|
277
|
+
}
|
|
278
|
+
// Regular paragraph — process inline templates
|
|
279
|
+
try {
|
|
280
|
+
const processed = processParagraph(block, ctx, `${location} para ${i}`);
|
|
281
|
+
result.push(processed);
|
|
282
|
+
}
|
|
283
|
+
catch (err) {
|
|
284
|
+
if (err instanceof TemplateError && err.tagName === undefined) {
|
|
285
|
+
throw new TemplateError(err.message, err.placeholder, err.location, {
|
|
286
|
+
cause: err.cause,
|
|
287
|
+
tagName: err.placeholder,
|
|
288
|
+
paragraphIndex: i,
|
|
289
|
+
sectionPath: location
|
|
290
|
+
});
|
|
291
|
+
}
|
|
292
|
+
throw err;
|
|
293
|
+
}
|
|
294
|
+
i++;
|
|
295
|
+
}
|
|
296
|
+
else if (block.type === "table") {
|
|
297
|
+
const processed = processTable(block, ctx, `${location} table ${i}`);
|
|
298
|
+
result.push(processed);
|
|
299
|
+
i++;
|
|
300
|
+
}
|
|
301
|
+
else if (block.type === "sdt") {
|
|
302
|
+
// Recursively process SDT content (StructuredDocumentTag)
|
|
303
|
+
const sdt = block;
|
|
304
|
+
const processedContent = processBodyContent(sdt.content, ctx, `${location} sdt ${i}`);
|
|
305
|
+
result.push({
|
|
306
|
+
...sdt,
|
|
307
|
+
content: processedContent
|
|
308
|
+
});
|
|
309
|
+
i++;
|
|
310
|
+
}
|
|
311
|
+
else {
|
|
312
|
+
result.push(block);
|
|
313
|
+
i++;
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
return result;
|
|
317
|
+
}
|
|
318
|
+
function processBlockList(blocks, ctx, location) {
|
|
319
|
+
// Reuse body content processing, filtering to Paragraph | Table
|
|
320
|
+
return processBodyContent(blocks, ctx, location);
|
|
321
|
+
}
|
|
322
|
+
// =============================================================================
|
|
323
|
+
// Paragraph processing (inline variable substitution)
|
|
324
|
+
// =============================================================================
|
|
325
|
+
function processParagraph(para, ctx, location) {
|
|
326
|
+
// Merge all run text, resolve inline variables, then rebuild runs
|
|
327
|
+
const fullText = extractParagraphText(para);
|
|
328
|
+
// Quick check: does this paragraph contain any delimiters?
|
|
329
|
+
if (!fullText.includes(ctx.open)) {
|
|
330
|
+
return para;
|
|
331
|
+
}
|
|
332
|
+
// Tokenize and evaluate inline expressions
|
|
333
|
+
const tokens = tokenize(fullText, ctx.open, ctx.close);
|
|
334
|
+
const resolved = evaluateInlineTokens(tokens, ctx, location);
|
|
335
|
+
// If the text didn't change, return as-is
|
|
336
|
+
if (resolved === fullText) {
|
|
337
|
+
return para;
|
|
338
|
+
}
|
|
339
|
+
// Rebuild the paragraph runs with the resolved text
|
|
340
|
+
return rebuildParagraphText(para, resolved);
|
|
341
|
+
}
|
|
342
|
+
/**
|
|
343
|
+
* Evaluate inline tokens (only variables and inline if/each within a single paragraph line).
|
|
344
|
+
* For simplicity, inline conditionals and loops are evaluated as text.
|
|
345
|
+
*/
|
|
346
|
+
function evaluateInlineTokens(tokens, ctx, location) {
|
|
347
|
+
let result = "";
|
|
348
|
+
let i = 0;
|
|
349
|
+
while (i < tokens.length) {
|
|
350
|
+
const token = tokens[i];
|
|
351
|
+
switch (token.type) {
|
|
352
|
+
case TokenType.Text:
|
|
353
|
+
result += token.value;
|
|
354
|
+
i++;
|
|
355
|
+
break;
|
|
356
|
+
case TokenType.Variable: {
|
|
357
|
+
const path = token.path;
|
|
358
|
+
let value;
|
|
359
|
+
if (path === "@index") {
|
|
360
|
+
value = resolvePath(ctx.data, "@index");
|
|
361
|
+
}
|
|
362
|
+
else {
|
|
363
|
+
value = resolvePath(ctx.data, path);
|
|
364
|
+
}
|
|
365
|
+
if (value === undefined && ctx.strict) {
|
|
366
|
+
throw new TemplateError(`Unresolved variable: {{${path}}}`, path, location, {
|
|
367
|
+
tagName: path,
|
|
368
|
+
sectionPath: location
|
|
369
|
+
});
|
|
370
|
+
}
|
|
371
|
+
result += valueToString(value);
|
|
372
|
+
i++;
|
|
373
|
+
break;
|
|
374
|
+
}
|
|
375
|
+
case TokenType.IfOpen: {
|
|
376
|
+
// Inline if: collect tokens until matching /if
|
|
377
|
+
const condValue = resolvePath(ctx.data, token.condition);
|
|
378
|
+
const { trueBranch, falseBranch, endIndex } = collectInlineIf(tokens, i);
|
|
379
|
+
if (endIndex === -1) {
|
|
380
|
+
throw new TemplateError(`Unclosed inline {{#if ${token.condition}}}`, `#if ${token.condition}`, location, { tagName: `#if ${token.condition}`, sectionPath: location });
|
|
381
|
+
}
|
|
382
|
+
if (isTruthy(condValue)) {
|
|
383
|
+
result += evaluateInlineTokens(trueBranch, ctx, location);
|
|
384
|
+
}
|
|
385
|
+
else {
|
|
386
|
+
result += evaluateInlineTokens(falseBranch, ctx, location);
|
|
387
|
+
}
|
|
388
|
+
i = endIndex + 1;
|
|
389
|
+
break;
|
|
390
|
+
}
|
|
391
|
+
case TokenType.EachOpen: {
|
|
392
|
+
// Inline each: collect tokens until matching /each
|
|
393
|
+
const items = resolvePath(ctx.data, token.collection);
|
|
394
|
+
const { body, endIndex } = collectInlineEach(tokens, i);
|
|
395
|
+
if (endIndex === -1) {
|
|
396
|
+
throw new TemplateError(`Unclosed inline {{#each ${token.collection}}}`, `#each ${token.collection}`, location, { tagName: `#each ${token.collection}`, sectionPath: location });
|
|
397
|
+
}
|
|
398
|
+
if (Array.isArray(items)) {
|
|
399
|
+
for (let idx = 0; idx < items.length; idx++) {
|
|
400
|
+
const itemData = buildLoopData(items[idx], idx, ctx.data);
|
|
401
|
+
const innerCtx = { ...ctx, data: itemData };
|
|
402
|
+
result += evaluateInlineTokens(body, innerCtx, location);
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
i = endIndex + 1;
|
|
406
|
+
break;
|
|
407
|
+
}
|
|
408
|
+
default:
|
|
409
|
+
// Stray else/close tokens — skip
|
|
410
|
+
i++;
|
|
411
|
+
break;
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
return result;
|
|
415
|
+
}
|
|
416
|
+
function collectInlineIf(tokens, startIdx) {
|
|
417
|
+
let depth = 0;
|
|
418
|
+
const trueBranch = [];
|
|
419
|
+
const falseBranch = [];
|
|
420
|
+
let inElse = false;
|
|
421
|
+
let endIndex = -1;
|
|
422
|
+
for (let i = startIdx + 1; i < tokens.length; i++) {
|
|
423
|
+
const t = tokens[i];
|
|
424
|
+
if (t.type === TokenType.IfOpen) {
|
|
425
|
+
depth++;
|
|
426
|
+
(inElse ? falseBranch : trueBranch).push(t);
|
|
427
|
+
}
|
|
428
|
+
else if (t.type === TokenType.IfClose) {
|
|
429
|
+
if (depth === 0) {
|
|
430
|
+
endIndex = i;
|
|
431
|
+
break;
|
|
432
|
+
}
|
|
433
|
+
depth--;
|
|
434
|
+
(inElse ? falseBranch : trueBranch).push(t);
|
|
435
|
+
}
|
|
436
|
+
else if (t.type === TokenType.Else && depth === 0) {
|
|
437
|
+
inElse = true;
|
|
438
|
+
}
|
|
439
|
+
else {
|
|
440
|
+
(inElse ? falseBranch : trueBranch).push(t);
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
return { trueBranch, falseBranch, endIndex };
|
|
444
|
+
}
|
|
445
|
+
function collectInlineEach(tokens, startIdx) {
|
|
446
|
+
let depth = 0;
|
|
447
|
+
const body = [];
|
|
448
|
+
let endIndex = -1;
|
|
449
|
+
for (let i = startIdx + 1; i < tokens.length; i++) {
|
|
450
|
+
const t = tokens[i];
|
|
451
|
+
if (t.type === TokenType.EachOpen) {
|
|
452
|
+
depth++;
|
|
453
|
+
body.push(t);
|
|
454
|
+
}
|
|
455
|
+
else if (t.type === TokenType.EachClose) {
|
|
456
|
+
if (depth === 0) {
|
|
457
|
+
endIndex = i;
|
|
458
|
+
break;
|
|
459
|
+
}
|
|
460
|
+
depth--;
|
|
461
|
+
body.push(t);
|
|
462
|
+
}
|
|
463
|
+
else {
|
|
464
|
+
body.push(t);
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
return { body, endIndex };
|
|
468
|
+
}
|
|
469
|
+
// =============================================================================
|
|
470
|
+
// Rebuild paragraph with new text content
|
|
471
|
+
// =============================================================================
|
|
472
|
+
/**
|
|
473
|
+
* Rebuild a paragraph's run text content with a new resolved string.
|
|
474
|
+
* Preserves the formatting of the first text run.
|
|
475
|
+
*/
|
|
476
|
+
function rebuildParagraphText(para, newText) {
|
|
477
|
+
// Find the first run with text content to use as formatting reference
|
|
478
|
+
let refProperties;
|
|
479
|
+
for (const child of para.children) {
|
|
480
|
+
if (!isRun(child)) {
|
|
481
|
+
continue;
|
|
482
|
+
}
|
|
483
|
+
for (const c of child.content) {
|
|
484
|
+
if (c.type === "text") {
|
|
485
|
+
refProperties = child.properties;
|
|
486
|
+
break;
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
if (refProperties !== undefined) {
|
|
490
|
+
break;
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
// If no text runs found, get properties from first run
|
|
494
|
+
if (refProperties === undefined) {
|
|
495
|
+
for (const child of para.children) {
|
|
496
|
+
if (isRun(child)) {
|
|
497
|
+
refProperties = child.properties;
|
|
498
|
+
break;
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
// Build new children:
|
|
503
|
+
// - replace the first Run we see with `newRun` (carrying the resolved text);
|
|
504
|
+
// - drop every other Run (their text was already concatenated into newText);
|
|
505
|
+
// - keep non-Run children (bookmarkStart / bookmarkEnd / commentRangeStart /
|
|
506
|
+
// commentRangeEnd / hyperlink / insertedRun / movedToRun / etc.) at
|
|
507
|
+
// their original index. Reordering these silently miswires bookmark and
|
|
508
|
+
// comment ranges, since the start/end markers stop bracketing the
|
|
509
|
+
// intended content.
|
|
510
|
+
const newRun = {
|
|
511
|
+
properties: refProperties,
|
|
512
|
+
content: [{ type: "text", text: newText }]
|
|
513
|
+
};
|
|
514
|
+
const newChildren = [];
|
|
515
|
+
let runReplaced = false;
|
|
516
|
+
for (const child of para.children) {
|
|
517
|
+
if (isRun(child)) {
|
|
518
|
+
if (!runReplaced) {
|
|
519
|
+
newChildren.push(newRun);
|
|
520
|
+
runReplaced = true;
|
|
521
|
+
}
|
|
522
|
+
// Subsequent runs are absorbed into newRun's text — drop them.
|
|
523
|
+
}
|
|
524
|
+
else {
|
|
525
|
+
newChildren.push(child);
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
// If the paragraph had no runs at all (rare — typically all bookmark
|
|
529
|
+
// markers), append newRun at the end so the rendered text still appears.
|
|
530
|
+
if (!runReplaced) {
|
|
531
|
+
newChildren.push(newRun);
|
|
532
|
+
}
|
|
533
|
+
return {
|
|
534
|
+
...para,
|
|
535
|
+
children: newChildren
|
|
536
|
+
};
|
|
537
|
+
}
|
|
538
|
+
// =============================================================================
|
|
539
|
+
// Table processing (row-level loops)
|
|
540
|
+
// =============================================================================
|
|
541
|
+
function processTable(table, ctx, location) {
|
|
542
|
+
const newRows = [];
|
|
543
|
+
let i = 0;
|
|
544
|
+
while (i < table.rows.length) {
|
|
545
|
+
const row = table.rows[i];
|
|
546
|
+
const rowText = extractRowText(row);
|
|
547
|
+
// Check if this row contains a #each directive
|
|
548
|
+
if (rowText.includes(ctx.open + "#each ") || rowText.includes(ctx.open + " #each ")) {
|
|
549
|
+
const collectionExpr = extractDirectiveFromText(rowText, ctx.open, ctx.close, "#each ");
|
|
550
|
+
if (collectionExpr) {
|
|
551
|
+
// Find the closing row
|
|
552
|
+
const endRowIdx = findClosingTableRow(table.rows, i, ctx);
|
|
553
|
+
if (endRowIdx === -1) {
|
|
554
|
+
throw new TemplateError(`Unclosed {{#each ${collectionExpr}}} in table row`, `#each ${collectionExpr}`, location, {
|
|
555
|
+
tagName: `#each ${collectionExpr}`,
|
|
556
|
+
paragraphIndex: i,
|
|
557
|
+
sectionPath: location
|
|
558
|
+
});
|
|
559
|
+
}
|
|
560
|
+
const items = resolvePath(ctx.data, collectionExpr);
|
|
561
|
+
if (Array.isArray(items)) {
|
|
562
|
+
// If the loop is on a single row (open and close on same row)
|
|
563
|
+
if (endRowIdx === i) {
|
|
564
|
+
for (let idx = 0; idx < items.length; idx++) {
|
|
565
|
+
const itemData = buildLoopData(items[idx], idx, ctx.data);
|
|
566
|
+
const innerCtx = { ...ctx, data: itemData };
|
|
567
|
+
const clonedRow = cloneRow(row);
|
|
568
|
+
const processedRow = processTableRow(clonedRow, innerCtx, location);
|
|
569
|
+
// Strip the #each and /each tags from the row
|
|
570
|
+
const cleanedRow = stripDirectivesFromRow(processedRow, ctx, "#each ", "/each");
|
|
571
|
+
newRows.push(cleanedRow);
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
else {
|
|
575
|
+
// Multi-row loop: duplicate all rows between open and close
|
|
576
|
+
const templateRows = table.rows.slice(i, endRowIdx + 1);
|
|
577
|
+
for (let idx = 0; idx < items.length; idx++) {
|
|
578
|
+
const itemData = buildLoopData(items[idx], idx, ctx.data);
|
|
579
|
+
const innerCtx = { ...ctx, data: itemData };
|
|
580
|
+
for (const templateRow of templateRows) {
|
|
581
|
+
const clonedRow = cloneRow(templateRow);
|
|
582
|
+
const processedRow = processTableRow(clonedRow, innerCtx, location);
|
|
583
|
+
const cleanedRow = stripDirectivesFromRow(processedRow, ctx, "#each ", "/each");
|
|
584
|
+
newRows.push(cleanedRow);
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
i = endRowIdx + 1;
|
|
590
|
+
continue;
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
// Regular row: process inline templates in each cell
|
|
594
|
+
const processedRow = processTableRow(row, ctx, location);
|
|
595
|
+
newRows.push(processedRow);
|
|
596
|
+
i++;
|
|
597
|
+
}
|
|
598
|
+
return {
|
|
599
|
+
...table,
|
|
600
|
+
rows: newRows
|
|
601
|
+
};
|
|
602
|
+
}
|
|
603
|
+
function processTableRow(row, ctx, location) {
|
|
604
|
+
const newCells = [];
|
|
605
|
+
for (const cell of row.cells) {
|
|
606
|
+
const newContent = [];
|
|
607
|
+
for (const block of cell.content) {
|
|
608
|
+
if (block.type === "paragraph") {
|
|
609
|
+
newContent.push(processParagraph(block, ctx, location));
|
|
610
|
+
}
|
|
611
|
+
else if (block.type === "table") {
|
|
612
|
+
newContent.push(processTable(block, ctx, location));
|
|
613
|
+
}
|
|
614
|
+
else {
|
|
615
|
+
newContent.push(block);
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
newCells.push({ ...cell, content: newContent });
|
|
619
|
+
}
|
|
620
|
+
return { ...row, cells: newCells };
|
|
621
|
+
}
|
|
622
|
+
function extractRowText(row) {
|
|
623
|
+
let text = "";
|
|
624
|
+
for (const cell of row.cells) {
|
|
625
|
+
for (const block of cell.content) {
|
|
626
|
+
if (block.type === "paragraph") {
|
|
627
|
+
text += extractParagraphText(block);
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
return text;
|
|
632
|
+
}
|
|
633
|
+
function findClosingTableRow(rows, startIdx, ctx) {
|
|
634
|
+
// We start AFTER having seen exactly one `{{#each ...}}` token, so the
|
|
635
|
+
// virtual depth at the boundary into row `startIdx` is 1. Each row can
|
|
636
|
+
// contain any mix of `{{#each ...}}` opens and `{{/each}}` closes; we
|
|
637
|
+
// increment the depth by `opens` and decrement by `closes`. The first
|
|
638
|
+
// row where depth hits zero is the closing row.
|
|
639
|
+
//
|
|
640
|
+
// A previous implementation lumped both adjustments together and
|
|
641
|
+
// returned as soon as `depth === 0 && closes > 0`, which mis-handled
|
|
642
|
+
// rows containing two opens and one close (depth ends at 1, but the
|
|
643
|
+
// intermediate calculation hit a transient zero).
|
|
644
|
+
let depth = 1;
|
|
645
|
+
for (let i = startIdx; i < rows.length; i++) {
|
|
646
|
+
const text = extractRowText(rows[i]);
|
|
647
|
+
let opens = countOccurrences(text, ctx.open + "#each ");
|
|
648
|
+
const closes = countOccurrences(text, ctx.open + "/each" + ctx.close);
|
|
649
|
+
// The opening `{{#each ...}}` of the current loop is in row
|
|
650
|
+
// startIdx; don't count it as opening a new nested loop.
|
|
651
|
+
if (i === startIdx && opens > 0) {
|
|
652
|
+
opens--;
|
|
653
|
+
}
|
|
654
|
+
// Walk the row token-by-token in opens-then-closes order. We don't
|
|
655
|
+
// know the actual interleave from raw counts, so we apply opens
|
|
656
|
+
// first, then closes — opens always come before their matching
|
|
657
|
+
// close in valid templates, and a row that closes more than it
|
|
658
|
+
// opens (other than the top-level close in this very row) is
|
|
659
|
+
// malformed input we should still terminate on.
|
|
660
|
+
depth += opens;
|
|
661
|
+
if (depth <= 0 && opens === 0 && closes === 0) {
|
|
662
|
+
// Defensive: should never happen because we entered with depth=1.
|
|
663
|
+
return -1;
|
|
664
|
+
}
|
|
665
|
+
if (closes >= depth) {
|
|
666
|
+
// Consume `depth` worth of closes in this row; the remainder, if
|
|
667
|
+
// any, is malformed but stops the search here.
|
|
668
|
+
return i;
|
|
669
|
+
}
|
|
670
|
+
depth -= closes;
|
|
671
|
+
}
|
|
672
|
+
return -1;
|
|
673
|
+
}
|
|
674
|
+
function stripDirectivesFromRow(row, ctx, openDir, closeDir) {
|
|
675
|
+
const newCells = [];
|
|
676
|
+
for (const cell of row.cells) {
|
|
677
|
+
const newContent = [];
|
|
678
|
+
for (const block of cell.content) {
|
|
679
|
+
if (block.type === "paragraph") {
|
|
680
|
+
const text = extractParagraphText(block);
|
|
681
|
+
// Remove directive tags
|
|
682
|
+
const openPattern = ctx.open + openDir;
|
|
683
|
+
const closePattern = ctx.open + closeDir + ctx.close;
|
|
684
|
+
if (text.includes(openPattern) || text.includes(closePattern)) {
|
|
685
|
+
let cleaned = text;
|
|
686
|
+
// Remove the entire {{#each ...}} tag
|
|
687
|
+
const eachOpenRegex = new RegExp(escapeRegex(ctx.open) +
|
|
688
|
+
"\\s*" +
|
|
689
|
+
escapeRegex(openDir) +
|
|
690
|
+
"[^" +
|
|
691
|
+
escapeRegex(ctx.close.charAt(0)) +
|
|
692
|
+
"]*" +
|
|
693
|
+
escapeRegex(ctx.close), "g");
|
|
694
|
+
cleaned = cleaned.replace(eachOpenRegex, "");
|
|
695
|
+
// Remove {{/each}}
|
|
696
|
+
const eachCloseRegex = new RegExp(escapeRegex(ctx.open) +
|
|
697
|
+
"\\s*" +
|
|
698
|
+
escapeRegex(closeDir) +
|
|
699
|
+
"\\s*" +
|
|
700
|
+
escapeRegex(ctx.close), "g");
|
|
701
|
+
cleaned = cleaned.replace(eachCloseRegex, "");
|
|
702
|
+
cleaned = cleaned.trim();
|
|
703
|
+
if (cleaned.length > 0) {
|
|
704
|
+
newContent.push(rebuildParagraphText(block, cleaned));
|
|
705
|
+
}
|
|
706
|
+
else {
|
|
707
|
+
// Keep at least one paragraph in a cell
|
|
708
|
+
newContent.push(rebuildParagraphText(block, ""));
|
|
709
|
+
}
|
|
710
|
+
}
|
|
711
|
+
else {
|
|
712
|
+
newContent.push(block);
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
else {
|
|
716
|
+
newContent.push(block);
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
newCells.push({
|
|
720
|
+
...cell,
|
|
721
|
+
content: newContent.length > 0 ? newContent : [{ type: "paragraph", children: [] }]
|
|
722
|
+
});
|
|
723
|
+
}
|
|
724
|
+
return { ...row, cells: newCells };
|
|
725
|
+
}
|
|
726
|
+
// =============================================================================
|
|
727
|
+
// Block-level directive helpers
|
|
728
|
+
// =============================================================================
|
|
729
|
+
function hasBlockDirective(text, open, close, directive) {
|
|
730
|
+
// Block-level directives must occupy the *entire* paragraph (modulo
|
|
731
|
+
// surrounding whitespace). A paragraph like "Hello {{#if x}}A{{/if}}"
|
|
732
|
+
// contains an *inline* directive and must not be re-classified as a block
|
|
733
|
+
// directive — otherwise the engine tries to find the block close on
|
|
734
|
+
// following paragraphs and emits "Unclosed {{#if x}}" errors.
|
|
735
|
+
const trimmed = text.trim();
|
|
736
|
+
const opener = open + directive;
|
|
737
|
+
if (!trimmed.startsWith(opener)) {
|
|
738
|
+
return false;
|
|
739
|
+
}
|
|
740
|
+
// The opener tag must close before any other text follows. e.g. valid:
|
|
741
|
+
// "{{#if x}}" → block
|
|
742
|
+
// "{{#if x}}\n" → block
|
|
743
|
+
// "{{#if x}}foo" → inline (has trailing content on same paragraph)
|
|
744
|
+
const tagEnd = trimmed.indexOf(close, opener.length);
|
|
745
|
+
if (tagEnd === -1) {
|
|
746
|
+
return false;
|
|
747
|
+
}
|
|
748
|
+
const afterTag = trimmed.slice(tagEnd + close.length).trim();
|
|
749
|
+
return afterTag.length === 0;
|
|
750
|
+
}
|
|
751
|
+
function extractDirectiveArg(text, open, close, directive) {
|
|
752
|
+
const startIdx = text.indexOf(open + directive);
|
|
753
|
+
if (startIdx === -1) {
|
|
754
|
+
// Try with space variations
|
|
755
|
+
const idx = text.indexOf(open + " " + directive);
|
|
756
|
+
if (idx === -1) {
|
|
757
|
+
return "";
|
|
758
|
+
}
|
|
759
|
+
const argStart = idx + open.length + 1 + directive.length;
|
|
760
|
+
const endIdx = text.indexOf(close, argStart);
|
|
761
|
+
if (endIdx === -1) {
|
|
762
|
+
return "";
|
|
763
|
+
}
|
|
764
|
+
return text.slice(argStart, endIdx).trim();
|
|
765
|
+
}
|
|
766
|
+
const argStart = startIdx + open.length + directive.length;
|
|
767
|
+
const endIdx = text.indexOf(close, argStart);
|
|
768
|
+
if (endIdx === -1) {
|
|
769
|
+
return "";
|
|
770
|
+
}
|
|
771
|
+
return text.slice(argStart, endIdx).trim();
|
|
772
|
+
}
|
|
773
|
+
function extractDirectiveFromText(text, open, close, directive) {
|
|
774
|
+
const pattern = open + directive;
|
|
775
|
+
const idx = text.indexOf(pattern);
|
|
776
|
+
if (idx === -1) {
|
|
777
|
+
return null;
|
|
778
|
+
}
|
|
779
|
+
const argStart = idx + pattern.length;
|
|
780
|
+
const endIdx = text.indexOf(close, argStart);
|
|
781
|
+
if (endIdx === -1) {
|
|
782
|
+
return null;
|
|
783
|
+
}
|
|
784
|
+
return text.slice(argStart, endIdx).trim();
|
|
785
|
+
}
|
|
786
|
+
function findClosingBlock(blocks, startIdx, ctx, openDir, closeDir) {
|
|
787
|
+
let depth = 0;
|
|
788
|
+
for (let i = startIdx + 1; i < blocks.length; i++) {
|
|
789
|
+
const block = blocks[i];
|
|
790
|
+
if (block.type !== "paragraph") {
|
|
791
|
+
continue;
|
|
792
|
+
}
|
|
793
|
+
const text = extractParagraphText(block);
|
|
794
|
+
if (hasBlockDirective(text, ctx.open, ctx.close, openDir)) {
|
|
795
|
+
depth++;
|
|
796
|
+
}
|
|
797
|
+
if (text.includes(ctx.open + closeDir + ctx.close) ||
|
|
798
|
+
text.includes(ctx.open + " " + closeDir + " " + ctx.close)) {
|
|
799
|
+
if (depth === 0) {
|
|
800
|
+
return i;
|
|
801
|
+
}
|
|
802
|
+
depth--;
|
|
803
|
+
}
|
|
804
|
+
}
|
|
805
|
+
return -1;
|
|
806
|
+
}
|
|
807
|
+
function findIfBlock(blocks, startIdx, ctx) {
|
|
808
|
+
let depth = 0;
|
|
809
|
+
let elseIdx = -1;
|
|
810
|
+
for (let i = startIdx + 1; i < blocks.length; i++) {
|
|
811
|
+
const block = blocks[i];
|
|
812
|
+
if (block.type !== "paragraph") {
|
|
813
|
+
continue;
|
|
814
|
+
}
|
|
815
|
+
const text = extractParagraphText(block);
|
|
816
|
+
// A single paragraph can legitimately contain both `{{#if cond}}` and
|
|
817
|
+
// `{{/if}}` (an inline if). Count opens and closes independently so the
|
|
818
|
+
// pairing logic stays correct in that case — the previous else-if chain
|
|
819
|
+
// would advance `depth` and then skip the close, leaving the block
|
|
820
|
+
// permanently unterminated.
|
|
821
|
+
const openCount = countOccurrences(text, ctx.open + "#if ");
|
|
822
|
+
const closeCount = countOccurrences(text, ctx.open + "/if" + ctx.close) +
|
|
823
|
+
countOccurrences(text, ctx.open + " /if " + ctx.close);
|
|
824
|
+
if (closeCount > 0) {
|
|
825
|
+
// Account for inline opens first, then balance closes against the
|
|
826
|
+
// outstanding depth.
|
|
827
|
+
depth += openCount;
|
|
828
|
+
for (let c = 0; c < closeCount; c++) {
|
|
829
|
+
if (depth === 0) {
|
|
830
|
+
return { elseIdx, endIdx: i };
|
|
831
|
+
}
|
|
832
|
+
depth--;
|
|
833
|
+
}
|
|
834
|
+
}
|
|
835
|
+
else if (openCount > 0) {
|
|
836
|
+
depth += openCount;
|
|
837
|
+
}
|
|
838
|
+
else if (depth === 0 &&
|
|
839
|
+
(text.trim() === ctx.open + "else" + ctx.close ||
|
|
840
|
+
text.includes(ctx.open + "else" + ctx.close))) {
|
|
841
|
+
elseIdx = i;
|
|
842
|
+
}
|
|
843
|
+
}
|
|
844
|
+
return { elseIdx, endIdx: -1 };
|
|
845
|
+
}
|
|
846
|
+
// =============================================================================
|
|
847
|
+
// Loop data construction
|
|
848
|
+
// =============================================================================
|
|
849
|
+
function buildLoopData(item, index, parentData) {
|
|
850
|
+
// Build the loop context on a null-prototype object so that an item
|
|
851
|
+
// like `{ "__proto__": { injected: "X" } }` (which JSON.parse will
|
|
852
|
+
// produce as an own enumerable data property — a known
|
|
853
|
+
// prototype-pollution vector) cannot retarget the result's prototype
|
|
854
|
+
// and leak hidden fields into the rendered document.
|
|
855
|
+
const result = Object.create(null);
|
|
856
|
+
for (const key of Object.keys(parentData)) {
|
|
857
|
+
if (key === "__proto__" || key === "constructor" || key === "prototype") {
|
|
858
|
+
// Skip dangerous keys at the parent level too.
|
|
859
|
+
continue;
|
|
860
|
+
}
|
|
861
|
+
result[key] = parentData[key];
|
|
862
|
+
}
|
|
863
|
+
result["@index"] = index;
|
|
864
|
+
result["."] = item;
|
|
865
|
+
// If item is an object, spread its OWN properties so {{.name}} and
|
|
866
|
+
// {{name}} both work. Skip the same dangerous key set defensively.
|
|
867
|
+
if (item != null && typeof item === "object" && !Array.isArray(item)) {
|
|
868
|
+
const obj = item;
|
|
869
|
+
for (const key of Object.keys(obj)) {
|
|
870
|
+
if (key === "__proto__" || key === "constructor" || key === "prototype") {
|
|
871
|
+
continue;
|
|
872
|
+
}
|
|
873
|
+
// Object.hasOwn ensures we never copy something that came from the
|
|
874
|
+
// prototype (Object.keys already returns only own enumerables, but
|
|
875
|
+
// belt-and-braces).
|
|
876
|
+
if (Object.hasOwn(obj, key)) {
|
|
877
|
+
result[key] = obj[key];
|
|
878
|
+
}
|
|
879
|
+
}
|
|
880
|
+
}
|
|
881
|
+
return result;
|
|
882
|
+
}
|
|
883
|
+
// =============================================================================
|
|
884
|
+
// Cloning helpers
|
|
885
|
+
// =============================================================================
|
|
886
|
+
function cloneBlocks(blocks) {
|
|
887
|
+
return structuredClone(blocks);
|
|
888
|
+
}
|
|
889
|
+
function cloneRow(row) {
|
|
890
|
+
return structuredClone(row);
|
|
891
|
+
}
|
|
892
|
+
// =============================================================================
|
|
893
|
+
// Utility
|
|
894
|
+
// =============================================================================
|
|
895
|
+
function countOccurrences(text, search) {
|
|
896
|
+
let count = 0;
|
|
897
|
+
let pos = 0;
|
|
898
|
+
while (true) {
|
|
899
|
+
const idx = text.indexOf(search, pos);
|
|
900
|
+
if (idx === -1) {
|
|
901
|
+
break;
|
|
902
|
+
}
|
|
903
|
+
count++;
|
|
904
|
+
pos = idx + search.length;
|
|
905
|
+
}
|
|
906
|
+
return count;
|
|
907
|
+
}
|
|
908
|
+
function escapeRegex(str) {
|
|
909
|
+
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
910
|
+
}
|
|
911
|
+
// =============================================================================
|
|
912
|
+
// Tag Listing: discover all template placeholders in a document
|
|
913
|
+
// =============================================================================
|
|
914
|
+
/**
|
|
915
|
+
* List all template tags found in a document.
|
|
916
|
+
*
|
|
917
|
+
* Scans body, headers, footers, footnotes, and endnotes for template
|
|
918
|
+
* placeholders. Useful for validating data objects against templates.
|
|
919
|
+
*
|
|
920
|
+
* @param doc - The parsed DocxDocument model.
|
|
921
|
+
* @param options - Optional settings (custom delimiters).
|
|
922
|
+
* @returns Array of all discovered template tags.
|
|
923
|
+
*/
|
|
924
|
+
export function listTemplateTags(doc, options) {
|
|
925
|
+
const open = options?.delimiters?.[0] ?? "{{";
|
|
926
|
+
const close = options?.delimiters?.[1] ?? "}}";
|
|
927
|
+
const tags = [];
|
|
928
|
+
function scanBlocks(blocks, location) {
|
|
929
|
+
for (let i = 0; i < blocks.length; i++) {
|
|
930
|
+
const block = blocks[i];
|
|
931
|
+
if (block.type === "paragraph") {
|
|
932
|
+
scanParagraph(block, `${location} paragraph ${i}`);
|
|
933
|
+
}
|
|
934
|
+
else if (block.type === "table") {
|
|
935
|
+
scanTable(block, `${location} table ${i}`);
|
|
936
|
+
}
|
|
937
|
+
}
|
|
938
|
+
}
|
|
939
|
+
function scanParagraph(para, location) {
|
|
940
|
+
const text = extractParagraphText(para);
|
|
941
|
+
scanText(text, location);
|
|
942
|
+
}
|
|
943
|
+
function scanTable(table, location) {
|
|
944
|
+
for (let r = 0; r < table.rows.length; r++) {
|
|
945
|
+
const row = table.rows[r];
|
|
946
|
+
for (let c = 0; c < row.cells.length; c++) {
|
|
947
|
+
const cell = row.cells[c];
|
|
948
|
+
for (let b = 0; b < cell.content.length; b++) {
|
|
949
|
+
const block = cell.content[b];
|
|
950
|
+
if (block.type === "paragraph") {
|
|
951
|
+
scanParagraph(block, `${location} row ${r} cell ${c}`);
|
|
952
|
+
}
|
|
953
|
+
else if (block.type === "table") {
|
|
954
|
+
scanTable(block, `${location} row ${r} cell ${c}`);
|
|
955
|
+
}
|
|
956
|
+
}
|
|
957
|
+
}
|
|
958
|
+
}
|
|
959
|
+
}
|
|
960
|
+
function scanText(text, location) {
|
|
961
|
+
let pos = 0;
|
|
962
|
+
while (pos < text.length) {
|
|
963
|
+
const startIdx = text.indexOf(open, pos);
|
|
964
|
+
if (startIdx === -1) {
|
|
965
|
+
break;
|
|
966
|
+
}
|
|
967
|
+
const endIdx = text.indexOf(close, startIdx + open.length);
|
|
968
|
+
if (endIdx === -1) {
|
|
969
|
+
break;
|
|
970
|
+
}
|
|
971
|
+
const expr = text.slice(startIdx + open.length, endIdx).trim();
|
|
972
|
+
pos = endIdx + close.length;
|
|
973
|
+
if (expr.startsWith("#if ")) {
|
|
974
|
+
tags.push({ expression: expr, type: "ifOpen", location });
|
|
975
|
+
}
|
|
976
|
+
else if (expr === "else") {
|
|
977
|
+
tags.push({ expression: expr, type: "else", location });
|
|
978
|
+
}
|
|
979
|
+
else if (expr === "/if") {
|
|
980
|
+
tags.push({ expression: expr, type: "ifClose", location });
|
|
981
|
+
}
|
|
982
|
+
else if (expr.startsWith("#each ")) {
|
|
983
|
+
tags.push({ expression: expr, type: "eachOpen", location });
|
|
984
|
+
}
|
|
985
|
+
else if (expr === "/each") {
|
|
986
|
+
tags.push({ expression: expr, type: "eachClose", location });
|
|
987
|
+
}
|
|
988
|
+
else if (expr.startsWith("#cols ")) {
|
|
989
|
+
tags.push({ expression: expr, type: "colsOpen", location });
|
|
990
|
+
}
|
|
991
|
+
else if (expr === "/cols") {
|
|
992
|
+
tags.push({ expression: expr, type: "colsClose", location });
|
|
993
|
+
}
|
|
994
|
+
else if (expr.startsWith("%")) {
|
|
995
|
+
tags.push({ expression: expr, type: "image", location });
|
|
996
|
+
}
|
|
997
|
+
else if (expr.startsWith("&")) {
|
|
998
|
+
tags.push({ expression: expr, type: "richText", location });
|
|
999
|
+
}
|
|
1000
|
+
else if (expr.startsWith(">")) {
|
|
1001
|
+
tags.push({ expression: expr, type: "subDocument", location });
|
|
1002
|
+
}
|
|
1003
|
+
else if (expr.startsWith("^")) {
|
|
1004
|
+
tags.push({ expression: expr, type: "chart", location });
|
|
1005
|
+
}
|
|
1006
|
+
else if (expr.startsWith("!")) {
|
|
1007
|
+
tags.push({ expression: expr, type: "htmlChunk", location });
|
|
1008
|
+
}
|
|
1009
|
+
else {
|
|
1010
|
+
tags.push({ expression: expr, type: "variable", location });
|
|
1011
|
+
}
|
|
1012
|
+
}
|
|
1013
|
+
}
|
|
1014
|
+
// Scan body
|
|
1015
|
+
scanBlocks(doc.body, "body");
|
|
1016
|
+
// Scan headers
|
|
1017
|
+
if (doc.headers) {
|
|
1018
|
+
for (const [key, headerDef] of doc.headers) {
|
|
1019
|
+
scanBlocks(headerDef.content.children, `header:${key}`);
|
|
1020
|
+
}
|
|
1021
|
+
}
|
|
1022
|
+
// Scan footers
|
|
1023
|
+
if (doc.footers) {
|
|
1024
|
+
for (const [key, footerDef] of doc.footers) {
|
|
1025
|
+
scanBlocks(footerDef.content.children, `footer:${key}`);
|
|
1026
|
+
}
|
|
1027
|
+
}
|
|
1028
|
+
// Scan footnotes
|
|
1029
|
+
if (doc.footnotes) {
|
|
1030
|
+
for (const note of doc.footnotes) {
|
|
1031
|
+
if (note.id <= 0) {
|
|
1032
|
+
continue;
|
|
1033
|
+
}
|
|
1034
|
+
scanBlocks(note.content, `footnote:${note.id}`);
|
|
1035
|
+
}
|
|
1036
|
+
}
|
|
1037
|
+
// Scan endnotes
|
|
1038
|
+
if (doc.endnotes) {
|
|
1039
|
+
for (const note of doc.endnotes) {
|
|
1040
|
+
if (note.id <= 0) {
|
|
1041
|
+
continue;
|
|
1042
|
+
}
|
|
1043
|
+
scanBlocks(note.content, `endnote:${note.id}`);
|
|
1044
|
+
}
|
|
1045
|
+
}
|
|
1046
|
+
return tags;
|
|
1047
|
+
}
|
|
1048
|
+
// =============================================================================
|
|
1049
|
+
// Enhanced value resolution for image/richText/subDocument
|
|
1050
|
+
// =============================================================================
|
|
1051
|
+
function isTemplateImage(v) {
|
|
1052
|
+
return v != null && typeof v === "object" && "image" in v && "width" in v && "height" in v;
|
|
1053
|
+
}
|
|
1054
|
+
function isTemplateRichText(v) {
|
|
1055
|
+
if (!Array.isArray(v)) {
|
|
1056
|
+
return false;
|
|
1057
|
+
}
|
|
1058
|
+
// Check that it looks like an array of Run objects (has .content array)
|
|
1059
|
+
return v.length > 0 && "content" in v[0] && Array.isArray(v[0].content);
|
|
1060
|
+
}
|
|
1061
|
+
function isTemplateSubDocument(v) {
|
|
1062
|
+
if (!Array.isArray(v)) {
|
|
1063
|
+
return false;
|
|
1064
|
+
}
|
|
1065
|
+
// Check that it looks like body content blocks (has .type)
|
|
1066
|
+
return v.length > 0 && "type" in v[0];
|
|
1067
|
+
}
|
|
1068
|
+
export function isTemplateChart(v) {
|
|
1069
|
+
return v != null && typeof v === "object" && "chart" in v && !("type" in v);
|
|
1070
|
+
}
|
|
1071
|
+
function isTemplateHtmlChunk(v) {
|
|
1072
|
+
return (v != null &&
|
|
1073
|
+
typeof v === "object" &&
|
|
1074
|
+
"html" in v &&
|
|
1075
|
+
typeof v.html === "string");
|
|
1076
|
+
}
|
|
1077
|
+
/**
|
|
1078
|
+
* Enhanced fillTemplate that supports image (%img), richText (&rt),
|
|
1079
|
+
* subDocument (>sub), and chart (^chart) placeholders in addition to the
|
|
1080
|
+
* standard variable, conditional, and loop directives.
|
|
1081
|
+
*
|
|
1082
|
+
* Image placeholder: {{%imagePath}} — value must be a TemplateImage object.
|
|
1083
|
+
* RichText placeholder: {{&richPath}} — value must be TemplateRichText (Run[]).
|
|
1084
|
+
* SubDocument placeholder: {{>subPath}} — value must be TemplateSubDocument (BodyContent[]).
|
|
1085
|
+
* Chart placeholder: {{^chartPath}} — value must be a TemplateChart object.
|
|
1086
|
+
*/
|
|
1087
|
+
export function fillTemplateEnhanced(doc, data, options) {
|
|
1088
|
+
const open = options?.delimiters?.[0] ?? "{{";
|
|
1089
|
+
const close = options?.delimiters?.[1] ?? "}}";
|
|
1090
|
+
const strict = options?.strict ?? true;
|
|
1091
|
+
// First pass: handle image / richText / subDocument in body
|
|
1092
|
+
const collectedImages = [];
|
|
1093
|
+
const newBody = processEnhancedBody(doc.body, data, open, close, strict, collectedImages);
|
|
1094
|
+
// Merge collected images into doc.images (avoid duplicates by fileName)
|
|
1095
|
+
const images = doc.images ? [...doc.images] : [];
|
|
1096
|
+
for (const img of collectedImages) {
|
|
1097
|
+
if (!images.some(existing => existing.fileName === img.fileName)) {
|
|
1098
|
+
images.push(img);
|
|
1099
|
+
}
|
|
1100
|
+
}
|
|
1101
|
+
// Second pass: standard fillTemplate. Note that `fillTemplate` mutates
|
|
1102
|
+
// its input doc in place (it edits `headerDef.content.children` and the
|
|
1103
|
+
// body array directly), so we hand it a deep-cloned wrapper to avoid
|
|
1104
|
+
// sneaking edits back into the caller's doc through shared references
|
|
1105
|
+
// (`modifiedDoc.headers === doc.headers` would otherwise propagate
|
|
1106
|
+
// mutations into `doc`).
|
|
1107
|
+
const modifiedDoc = {
|
|
1108
|
+
...doc,
|
|
1109
|
+
body: newBody,
|
|
1110
|
+
images: images.length > 0 ? images : undefined,
|
|
1111
|
+
...(doc.headers
|
|
1112
|
+
? {
|
|
1113
|
+
headers: new Map(Array.from(doc.headers, ([k, h]) => [
|
|
1114
|
+
k,
|
|
1115
|
+
{
|
|
1116
|
+
...h,
|
|
1117
|
+
content: { ...h.content, children: [...h.content.children] }
|
|
1118
|
+
}
|
|
1119
|
+
]))
|
|
1120
|
+
}
|
|
1121
|
+
: {}),
|
|
1122
|
+
...(doc.footers
|
|
1123
|
+
? {
|
|
1124
|
+
footers: new Map(Array.from(doc.footers, ([k, f]) => [
|
|
1125
|
+
k,
|
|
1126
|
+
{
|
|
1127
|
+
...f,
|
|
1128
|
+
content: { ...f.content, children: [...f.content.children] }
|
|
1129
|
+
}
|
|
1130
|
+
]))
|
|
1131
|
+
}
|
|
1132
|
+
: {}),
|
|
1133
|
+
...(doc.footnotes
|
|
1134
|
+
? { footnotes: doc.footnotes.map(fn => ({ ...fn, content: [...fn.content] })) }
|
|
1135
|
+
: {}),
|
|
1136
|
+
...(doc.endnotes
|
|
1137
|
+
? { endnotes: doc.endnotes.map(en => ({ ...en, content: [...en.content] })) }
|
|
1138
|
+
: {})
|
|
1139
|
+
};
|
|
1140
|
+
return fillTemplate(modifiedDoc, data, options);
|
|
1141
|
+
}
|
|
1142
|
+
function processEnhancedBody(blocks, data, open, close, strict, collectedImages) {
|
|
1143
|
+
const result = [];
|
|
1144
|
+
for (let blockIndex = 0; blockIndex < blocks.length; blockIndex++) {
|
|
1145
|
+
const block = blocks[blockIndex];
|
|
1146
|
+
if (block.type === "paragraph") {
|
|
1147
|
+
const text = extractParagraphText(block);
|
|
1148
|
+
// Check for HTML chunk placeholder ({{!htmlPath}})
|
|
1149
|
+
const htmlMatch = matchSinglePlaceholder(text, open, close, "!");
|
|
1150
|
+
if (htmlMatch) {
|
|
1151
|
+
const value = resolvePath(data, htmlMatch);
|
|
1152
|
+
if (isTemplateHtmlChunk(value)) {
|
|
1153
|
+
const seq = nextAltChunkSeq();
|
|
1154
|
+
const altChunk = {
|
|
1155
|
+
type: "altChunk",
|
|
1156
|
+
// Use a process-monotonic sequence so two `fillTemplate`
|
|
1157
|
+
// calls on the same template don't collide on rId or path.
|
|
1158
|
+
rId: `rId_altchunk_${seq}`,
|
|
1159
|
+
contentType: value.contentType ?? "text/html",
|
|
1160
|
+
data: utf8Encoder.encode(value.html),
|
|
1161
|
+
fileName: `afchunk${seq}.html`
|
|
1162
|
+
};
|
|
1163
|
+
result.push(altChunk);
|
|
1164
|
+
continue;
|
|
1165
|
+
}
|
|
1166
|
+
}
|
|
1167
|
+
// Check for sub-document placeholder (entire paragraph is the placeholder)
|
|
1168
|
+
const subMatch = matchSinglePlaceholder(text, open, close, ">");
|
|
1169
|
+
if (subMatch) {
|
|
1170
|
+
const value = resolvePath(data, subMatch);
|
|
1171
|
+
if (isTemplateSubDocument(value)) {
|
|
1172
|
+
result.push(...value);
|
|
1173
|
+
continue;
|
|
1174
|
+
}
|
|
1175
|
+
}
|
|
1176
|
+
// Check for image placeholder (with optional conditional prefix)
|
|
1177
|
+
const imgMatch = matchSinglePlaceholder(text, open, close, "%");
|
|
1178
|
+
if (imgMatch) {
|
|
1179
|
+
// Check for conditional image: {{%?condPath.imagePath}}
|
|
1180
|
+
if (imgMatch.startsWith("?")) {
|
|
1181
|
+
const rest = imgMatch.slice(1); // remove leading '?'
|
|
1182
|
+
const dotIdx = rest.indexOf(".");
|
|
1183
|
+
if (dotIdx !== -1) {
|
|
1184
|
+
const condPath = rest.slice(0, dotIdx);
|
|
1185
|
+
const imagePath = rest.slice(dotIdx + 1);
|
|
1186
|
+
const condValue = resolvePath(data, condPath);
|
|
1187
|
+
if (!isTruthy(condValue)) {
|
|
1188
|
+
// Condition is falsy — skip, don't insert anything
|
|
1189
|
+
continue;
|
|
1190
|
+
}
|
|
1191
|
+
// Condition is truthy — resolve the image
|
|
1192
|
+
const value = resolvePath(data, imagePath);
|
|
1193
|
+
if (isTemplateImage(value)) {
|
|
1194
|
+
const safeFileName = sanitizeTemplateImageFileName(value.image.fileName);
|
|
1195
|
+
const rId = value.image.rId ?? `rId_tpl_${nextTemplateImageSeq()}`;
|
|
1196
|
+
const imgContent = {
|
|
1197
|
+
type: "image",
|
|
1198
|
+
rId,
|
|
1199
|
+
width: value.width,
|
|
1200
|
+
height: value.height,
|
|
1201
|
+
altText: value.altText ?? safeFileName,
|
|
1202
|
+
name: safeFileName
|
|
1203
|
+
};
|
|
1204
|
+
const newPara = {
|
|
1205
|
+
type: "paragraph",
|
|
1206
|
+
properties: block.properties,
|
|
1207
|
+
children: [{ content: [imgContent] }]
|
|
1208
|
+
};
|
|
1209
|
+
result.push(newPara);
|
|
1210
|
+
collectedImages.push({ ...value.image, rId, fileName: safeFileName });
|
|
1211
|
+
continue;
|
|
1212
|
+
}
|
|
1213
|
+
}
|
|
1214
|
+
}
|
|
1215
|
+
// Standard image placeholder (no conditional)
|
|
1216
|
+
const value = resolvePath(data, imgMatch);
|
|
1217
|
+
if (isTemplateImage(value)) {
|
|
1218
|
+
const safeFileName = sanitizeTemplateImageFileName(value.image.fileName);
|
|
1219
|
+
const rId = value.image.rId ?? `rId_tpl_${nextTemplateImageSeq()}`;
|
|
1220
|
+
const imgContent = {
|
|
1221
|
+
type: "image",
|
|
1222
|
+
rId,
|
|
1223
|
+
width: value.width,
|
|
1224
|
+
height: value.height,
|
|
1225
|
+
altText: value.altText ?? safeFileName,
|
|
1226
|
+
name: safeFileName
|
|
1227
|
+
};
|
|
1228
|
+
const newPara = {
|
|
1229
|
+
type: "paragraph",
|
|
1230
|
+
properties: block.properties,
|
|
1231
|
+
children: [{ content: [imgContent] }]
|
|
1232
|
+
};
|
|
1233
|
+
result.push(newPara);
|
|
1234
|
+
// Register image for packaging
|
|
1235
|
+
collectedImages.push({ ...value.image, rId, fileName: safeFileName });
|
|
1236
|
+
continue;
|
|
1237
|
+
}
|
|
1238
|
+
}
|
|
1239
|
+
// Check for richText placeholder
|
|
1240
|
+
const rtMatch = matchSinglePlaceholder(text, open, close, "&");
|
|
1241
|
+
if (rtMatch) {
|
|
1242
|
+
const value = resolvePath(data, rtMatch);
|
|
1243
|
+
if (isTemplateRichText(value)) {
|
|
1244
|
+
const newPara = {
|
|
1245
|
+
type: "paragraph",
|
|
1246
|
+
properties: block.properties,
|
|
1247
|
+
children: [...value]
|
|
1248
|
+
};
|
|
1249
|
+
result.push(newPara);
|
|
1250
|
+
continue;
|
|
1251
|
+
}
|
|
1252
|
+
}
|
|
1253
|
+
// Check for chart placeholder
|
|
1254
|
+
const chartMatch = matchSinglePlaceholder(text, open, close, "^");
|
|
1255
|
+
if (chartMatch) {
|
|
1256
|
+
const value = resolvePath(data, chartMatch);
|
|
1257
|
+
if (isTemplateChart(value)) {
|
|
1258
|
+
const chartContent = {
|
|
1259
|
+
type: "chart",
|
|
1260
|
+
chart: value.chart,
|
|
1261
|
+
altText: value.altText,
|
|
1262
|
+
name: value.name
|
|
1263
|
+
};
|
|
1264
|
+
result.push(chartContent);
|
|
1265
|
+
continue;
|
|
1266
|
+
}
|
|
1267
|
+
}
|
|
1268
|
+
result.push(block);
|
|
1269
|
+
}
|
|
1270
|
+
else if (block.type === "table") {
|
|
1271
|
+
// Process table column loops
|
|
1272
|
+
const processed = processTableColumnLoop(block, data, open, close, strict);
|
|
1273
|
+
result.push(processed);
|
|
1274
|
+
}
|
|
1275
|
+
else if (block.type === "sdt") {
|
|
1276
|
+
// Recursively process SDT content
|
|
1277
|
+
const sdt = block;
|
|
1278
|
+
const processedContent = processEnhancedBody(sdt.content, data, open, close, strict, collectedImages);
|
|
1279
|
+
result.push({
|
|
1280
|
+
...sdt,
|
|
1281
|
+
content: processedContent
|
|
1282
|
+
});
|
|
1283
|
+
}
|
|
1284
|
+
else {
|
|
1285
|
+
result.push(block);
|
|
1286
|
+
}
|
|
1287
|
+
}
|
|
1288
|
+
return result;
|
|
1289
|
+
}
|
|
1290
|
+
/** Match a single-placeholder paragraph: {{<prefix><path>}} */
|
|
1291
|
+
function matchSinglePlaceholder(text, open, close, prefix) {
|
|
1292
|
+
const trimmed = text.trim();
|
|
1293
|
+
if (!trimmed.startsWith(open) || !trimmed.endsWith(close)) {
|
|
1294
|
+
return null;
|
|
1295
|
+
}
|
|
1296
|
+
const inner = trimmed.slice(open.length, trimmed.length - close.length).trim();
|
|
1297
|
+
if (!inner.startsWith(prefix)) {
|
|
1298
|
+
return null;
|
|
1299
|
+
}
|
|
1300
|
+
return inner.slice(prefix.length).trim();
|
|
1301
|
+
}
|
|
1302
|
+
// =============================================================================
|
|
1303
|
+
// Table Column Loop: {{#cols items}}...{{/cols}}
|
|
1304
|
+
// =============================================================================
|
|
1305
|
+
function processTableColumnLoop(table, data, open, close, _strict) {
|
|
1306
|
+
// Check if any cell in the first row contains a #cols directive
|
|
1307
|
+
if (table.rows.length === 0) {
|
|
1308
|
+
return table;
|
|
1309
|
+
}
|
|
1310
|
+
const firstRow = table.rows[0];
|
|
1311
|
+
let colsExpr = null;
|
|
1312
|
+
let templateColIdx = -1;
|
|
1313
|
+
for (let c = 0; c < firstRow.cells.length; c++) {
|
|
1314
|
+
const cell = firstRow.cells[c];
|
|
1315
|
+
for (const block of cell.content) {
|
|
1316
|
+
if (block.type === "paragraph") {
|
|
1317
|
+
const text = extractParagraphText(block);
|
|
1318
|
+
const pattern = open + "#cols ";
|
|
1319
|
+
if (text.includes(pattern)) {
|
|
1320
|
+
colsExpr = extractDirectiveFromText(text, open, close, "#cols ");
|
|
1321
|
+
templateColIdx = c;
|
|
1322
|
+
break;
|
|
1323
|
+
}
|
|
1324
|
+
}
|
|
1325
|
+
}
|
|
1326
|
+
if (colsExpr) {
|
|
1327
|
+
break;
|
|
1328
|
+
}
|
|
1329
|
+
}
|
|
1330
|
+
if (!colsExpr || templateColIdx === -1) {
|
|
1331
|
+
return table;
|
|
1332
|
+
}
|
|
1333
|
+
// Find closing {{/cols}} column
|
|
1334
|
+
let closeColIdx = templateColIdx;
|
|
1335
|
+
for (let c = templateColIdx; c < firstRow.cells.length; c++) {
|
|
1336
|
+
const cell = firstRow.cells[c];
|
|
1337
|
+
for (const block of cell.content) {
|
|
1338
|
+
if (block.type === "paragraph") {
|
|
1339
|
+
const text = extractParagraphText(block);
|
|
1340
|
+
if (text.includes(open + "/cols" + close) || text.includes(open + " /cols " + close)) {
|
|
1341
|
+
closeColIdx = c;
|
|
1342
|
+
break;
|
|
1343
|
+
}
|
|
1344
|
+
}
|
|
1345
|
+
}
|
|
1346
|
+
}
|
|
1347
|
+
const items = resolvePath(data, colsExpr);
|
|
1348
|
+
if (!Array.isArray(items)) {
|
|
1349
|
+
return table;
|
|
1350
|
+
}
|
|
1351
|
+
// For each row, duplicate the template columns for each item
|
|
1352
|
+
const newRows = [];
|
|
1353
|
+
for (const row of table.rows) {
|
|
1354
|
+
const beforeCells = row.cells.slice(0, templateColIdx);
|
|
1355
|
+
const templateCells = row.cells.slice(templateColIdx, closeColIdx + 1);
|
|
1356
|
+
const afterCells = row.cells.slice(closeColIdx + 1);
|
|
1357
|
+
const expandedCells = [...beforeCells];
|
|
1358
|
+
for (let idx = 0; idx < items.length; idx++) {
|
|
1359
|
+
for (const templateCell of templateCells) {
|
|
1360
|
+
const clonedCell = structuredClone(templateCell);
|
|
1361
|
+
// Strip #cols and /cols directives from text
|
|
1362
|
+
for (let b = 0; b < clonedCell.content.length; b++) {
|
|
1363
|
+
const block = clonedCell.content[b];
|
|
1364
|
+
if (block.type === "paragraph") {
|
|
1365
|
+
const text = extractParagraphText(block);
|
|
1366
|
+
let cleaned = text;
|
|
1367
|
+
const colsOpenRegex = new RegExp(escapeRegex(open) +
|
|
1368
|
+
"\\s*#cols\\s+[^" +
|
|
1369
|
+
escapeRegex(close.charAt(0)) +
|
|
1370
|
+
"]*" +
|
|
1371
|
+
escapeRegex(close), "g");
|
|
1372
|
+
cleaned = cleaned.replace(colsOpenRegex, "");
|
|
1373
|
+
const colsCloseRegex = new RegExp(escapeRegex(open) + "\\s*/cols\\s*" + escapeRegex(close), "g");
|
|
1374
|
+
cleaned = cleaned.replace(colsCloseRegex, "");
|
|
1375
|
+
// Resolve item variables
|
|
1376
|
+
const itemData = buildLoopData(items[idx], idx, data);
|
|
1377
|
+
const tokens = tokenize(cleaned, open, close);
|
|
1378
|
+
const ctx = { open, close, strict: false, data: itemData };
|
|
1379
|
+
const resolved = evaluateInlineTokens(tokens, ctx, "colLoop");
|
|
1380
|
+
if (resolved !== text) {
|
|
1381
|
+
clonedCell.content[b] = rebuildParagraphText(block, resolved);
|
|
1382
|
+
}
|
|
1383
|
+
else if (cleaned !== text) {
|
|
1384
|
+
clonedCell.content[b] = rebuildParagraphText(block, cleaned);
|
|
1385
|
+
}
|
|
1386
|
+
}
|
|
1387
|
+
}
|
|
1388
|
+
expandedCells.push(clonedCell);
|
|
1389
|
+
}
|
|
1390
|
+
}
|
|
1391
|
+
expandedCells.push(...afterCells);
|
|
1392
|
+
// Update grid (tblGrid) if needed: we'll leave that to the caller
|
|
1393
|
+
newRows.push({ ...row, cells: expandedCells });
|
|
1394
|
+
}
|
|
1395
|
+
return { ...table, rows: newRows };
|
|
1396
|
+
}
|
|
1397
|
+
// =============================================================================
|
|
1398
|
+
// rId allocation helpers
|
|
1399
|
+
// =============================================================================
|
|
1400
|
+
//
|
|
1401
|
+
// Template-generated parts (alt chunks, dynamic images) need stable but
|
|
1402
|
+
// non-colliding rIds. Earlier versions of this module derived the rId
|
|
1403
|
+
// from `blockIndex` or the image's `fileName`, which collided as soon as
|
|
1404
|
+
// the same template was filled twice or two distinct images shared a
|
|
1405
|
+
// file name. We now use module-level monotonic counters so each generated
|
|
1406
|
+
// part gets a unique rId for the lifetime of the process.
|
|
1407
|
+
let _altChunkSeq = 0;
|
|
1408
|
+
let _templateImageSeq = 0;
|
|
1409
|
+
function nextAltChunkSeq() {
|
|
1410
|
+
_altChunkSeq++;
|
|
1411
|
+
return _altChunkSeq;
|
|
1412
|
+
}
|
|
1413
|
+
function nextTemplateImageSeq() {
|
|
1414
|
+
_templateImageSeq++;
|
|
1415
|
+
return _templateImageSeq;
|
|
1416
|
+
}
|
|
1417
|
+
/**
|
|
1418
|
+
* Strip path-traversal segments and other unsafe characters from a
|
|
1419
|
+
* caller-supplied image file name so it can be safely used as a ZIP
|
|
1420
|
+
* entry path inside the package. Mirrors the equivalent helper in
|
|
1421
|
+
* `convert/odt/odt.ts`.
|
|
1422
|
+
*/
|
|
1423
|
+
function sanitizeTemplateImageFileName(raw) {
|
|
1424
|
+
if (!raw) {
|
|
1425
|
+
return "image.bin";
|
|
1426
|
+
}
|
|
1427
|
+
const lastSep = Math.max(raw.lastIndexOf("/"), raw.lastIndexOf("\\"));
|
|
1428
|
+
let leaf = lastSep >= 0 ? raw.substring(lastSep + 1) : raw;
|
|
1429
|
+
while (leaf.startsWith(".")) {
|
|
1430
|
+
leaf = leaf.substring(1);
|
|
1431
|
+
}
|
|
1432
|
+
leaf = leaf.replace(/[^A-Za-z0-9._-]/g, "_");
|
|
1433
|
+
leaf = leaf.replace(/\.{2,}/g, ".");
|
|
1434
|
+
return leaf || "image.bin";
|
|
1435
|
+
}
|