@cj-tech-master/excelts 9.6.1 → 10.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +18 -3
- package/README_zh.md +18 -3
- package/dist/browser/modules/excel/cell.d.ts +4 -0
- package/dist/browser/modules/excel/note.js +5 -1
- package/dist/browser/modules/excel/row.js +35 -2
- package/dist/browser/modules/excel/stream/workbook-writer.browser.d.ts +8 -1
- package/dist/browser/modules/excel/stream/workbook-writer.browser.js +22 -2
- package/dist/browser/modules/excel/types.d.ts +81 -0
- package/dist/browser/modules/excel/utils/drawing-utils.d.ts +8 -0
- package/dist/browser/modules/excel/utils/drawing-utils.js +19 -2
- package/dist/browser/modules/excel/workbook.browser.d.ts +16 -0
- package/dist/browser/modules/excel/workbook.browser.js +32 -2
- package/dist/browser/modules/excel/worksheet.d.ts +31 -1
- package/dist/browser/modules/excel/worksheet.js +83 -0
- package/dist/browser/modules/excel/xlsx/xform/comment/vml-shape-xform.d.ts +7 -0
- package/dist/browser/modules/excel/xlsx/xform/comment/vml-shape-xform.js +42 -8
- package/dist/browser/modules/excel/xlsx/xform/core/content-types-xform.js +3 -1
- package/dist/browser/modules/excel/xlsx/xform/drawing/absolute-anchor-xform.js +5 -0
- package/dist/browser/modules/excel/xlsx/xform/drawing/base-cell-anchor-xform.js +18 -1
- package/dist/browser/modules/excel/xlsx/xform/drawing/blip-xform.d.ts +6 -0
- package/dist/browser/modules/excel/xlsx/xform/drawing/blip-xform.js +38 -11
- package/dist/browser/modules/excel/xlsx/xform/drawing/one-cell-anchor-xform.d.ts +1 -0
- package/dist/browser/modules/excel/xlsx/xform/drawing/one-cell-anchor-xform.js +5 -0
- package/dist/browser/modules/excel/xlsx/xform/drawing/pic-xform.d.ts +2 -0
- package/dist/browser/modules/excel/xlsx/xform/drawing/pic-xform.js +2 -1
- package/dist/browser/modules/excel/xlsx/xform/drawing/shape-xform.d.ts +47 -0
- package/dist/browser/modules/excel/xlsx/xform/drawing/shape-xform.js +109 -0
- package/dist/browser/modules/excel/xlsx/xform/drawing/two-cell-anchor-xform.js +10 -1
- package/dist/browser/modules/excel/xlsx/xform/sheet/worksheet-xform.js +64 -1
- package/dist/browser/modules/pdf/builder/document-builder.js +22 -49
- package/dist/browser/modules/pdf/builder/pdf-editor.js +1 -1
- package/dist/browser/modules/pdf/core/pdf-stream.d.ts +28 -1
- package/dist/browser/modules/pdf/core/pdf-stream.js +38 -2
- package/dist/browser/modules/pdf/font/font-manager.d.ts +26 -0
- package/dist/browser/modules/pdf/font/font-manager.js +35 -18
- package/dist/browser/modules/pdf/render/page-renderer.d.ts +51 -3
- package/dist/browser/modules/pdf/render/page-renderer.js +111 -18
- package/dist/browser/modules/word/advanced/field-engine.js +45 -20
- package/dist/browser/modules/word/advanced/glossary.d.ts +10 -36
- package/dist/browser/modules/word/advanced/glossary.js +8 -9
- package/dist/browser/modules/word/advanced/math-convert.js +94 -12
- package/dist/browser/modules/word/advanced/ole-objects.d.ts +28 -0
- package/dist/browser/modules/word/advanced/ole-objects.js +122 -19
- package/dist/browser/modules/word/advanced/style-map.js +31 -10
- package/dist/browser/modules/word/builder/run-builders.d.ts +7 -1
- package/dist/browser/modules/word/builder/run-builders.js +7 -1
- package/dist/browser/modules/word/constants.d.ts +4 -0
- package/dist/browser/modules/word/constants.js +5 -1
- package/dist/browser/modules/word/convert/docx-to-semantic.d.ts +2 -1
- package/dist/browser/modules/word/convert/docx-to-semantic.js +135 -1
- package/dist/browser/modules/word/convert/html/html-import.d.ts +32 -1
- package/dist/browser/modules/word/convert/html/html-import.js +167 -14
- package/dist/browser/modules/word/convert/html/html.d.ts +2 -2
- package/dist/browser/modules/word/convert/html/html.js +1 -1
- package/dist/browser/modules/word/convert/markdown/markdown-import.d.ts +48 -18
- package/dist/browser/modules/word/convert/markdown/markdown-import.js +279 -69
- package/dist/browser/modules/word/convert/markdown/markdown.d.ts +1 -1
- package/dist/browser/modules/word/convert/odt/odt.js +407 -56
- package/dist/browser/modules/word/html.d.ts +2 -2
- package/dist/browser/modules/word/html.js +1 -1
- package/dist/browser/modules/word/index.base.d.ts +3 -3
- package/dist/browser/modules/word/index.base.js +1 -1
- package/dist/browser/modules/word/layout/layout-full.js +326 -19
- package/dist/browser/modules/word/layout/render-page.js +35 -8
- package/dist/browser/modules/word/markdown.d.ts +1 -1
- package/dist/browser/modules/word/query/compat.d.ts +10 -2
- package/dist/browser/modules/word/query/compat.js +29 -21
- package/dist/browser/modules/word/reader/docx-reader.js +105 -2
- package/dist/browser/modules/word/reader/math-parser.js +8 -2
- package/dist/browser/modules/word/security/cfb-reader.js +5 -5
- package/dist/browser/modules/word/types.d.ts +96 -1
- package/dist/browser/modules/word/writer/docx-packager.js +108 -2
- package/dist/browser/modules/word/writer/glossary-writer.d.ts +28 -0
- package/dist/browser/modules/word/writer/glossary-writer.js +121 -0
- package/dist/browser/modules/word/writer/header-footer-writer.js +105 -20
- package/dist/browser/modules/word/writer/math-writer.js +7 -2
- package/dist/browser/utils/font-metrics.d.ts +8 -0
- package/dist/browser/utils/font-metrics.js +43 -0
- package/dist/browser/utils/theme-colors.js +4 -1
- package/dist/cjs/modules/excel/note.js +5 -1
- package/dist/cjs/modules/excel/row.js +35 -2
- package/dist/cjs/modules/excel/stream/workbook-writer.browser.js +22 -2
- package/dist/cjs/modules/excel/utils/drawing-utils.js +19 -2
- package/dist/cjs/modules/excel/workbook.browser.js +31 -1
- package/dist/cjs/modules/excel/worksheet.js +83 -0
- package/dist/cjs/modules/excel/xlsx/xform/comment/vml-shape-xform.js +42 -8
- package/dist/cjs/modules/excel/xlsx/xform/core/content-types-xform.js +3 -1
- package/dist/cjs/modules/excel/xlsx/xform/drawing/absolute-anchor-xform.js +5 -0
- package/dist/cjs/modules/excel/xlsx/xform/drawing/base-cell-anchor-xform.js +18 -1
- package/dist/cjs/modules/excel/xlsx/xform/drawing/blip-xform.js +38 -11
- package/dist/cjs/modules/excel/xlsx/xform/drawing/one-cell-anchor-xform.js +5 -0
- package/dist/cjs/modules/excel/xlsx/xform/drawing/pic-xform.js +2 -1
- package/dist/cjs/modules/excel/xlsx/xform/drawing/shape-xform.js +112 -0
- package/dist/cjs/modules/excel/xlsx/xform/drawing/two-cell-anchor-xform.js +10 -1
- package/dist/cjs/modules/excel/xlsx/xform/sheet/worksheet-xform.js +64 -1
- package/dist/cjs/modules/pdf/builder/document-builder.js +21 -48
- package/dist/cjs/modules/pdf/builder/pdf-editor.js +1 -1
- package/dist/cjs/modules/pdf/core/pdf-stream.js +38 -2
- package/dist/cjs/modules/pdf/font/font-manager.js +35 -18
- package/dist/cjs/modules/pdf/render/page-renderer.js +112 -18
- package/dist/cjs/modules/word/advanced/field-engine.js +45 -20
- package/dist/cjs/modules/word/advanced/glossary.js +8 -9
- package/dist/cjs/modules/word/advanced/math-convert.js +94 -12
- package/dist/cjs/modules/word/advanced/ole-objects.js +123 -19
- package/dist/cjs/modules/word/advanced/style-map.js +31 -10
- package/dist/cjs/modules/word/builder/run-builders.js +7 -1
- package/dist/cjs/modules/word/constants.js +5 -1
- package/dist/cjs/modules/word/convert/docx-to-semantic.js +135 -1
- package/dist/cjs/modules/word/convert/html/html-import.js +168 -14
- package/dist/cjs/modules/word/convert/html/html.js +2 -1
- package/dist/cjs/modules/word/convert/markdown/markdown-import.js +279 -69
- package/dist/cjs/modules/word/convert/odt/odt.js +407 -56
- package/dist/cjs/modules/word/html.js +2 -1
- package/dist/cjs/modules/word/index.base.js +4 -3
- package/dist/cjs/modules/word/layout/layout-full.js +325 -18
- package/dist/cjs/modules/word/layout/render-page.js +35 -8
- package/dist/cjs/modules/word/query/compat.js +29 -21
- package/dist/cjs/modules/word/reader/docx-reader.js +104 -1
- package/dist/cjs/modules/word/reader/math-parser.js +8 -2
- package/dist/cjs/modules/word/security/cfb-reader.js +5 -5
- package/dist/cjs/modules/word/writer/docx-packager.js +108 -2
- package/dist/cjs/modules/word/writer/glossary-writer.js +124 -0
- package/dist/cjs/modules/word/writer/header-footer-writer.js +105 -20
- package/dist/cjs/modules/word/writer/math-writer.js +7 -2
- package/dist/cjs/utils/font-metrics.js +44 -0
- package/dist/cjs/utils/theme-colors.js +4 -1
- package/dist/esm/modules/excel/note.js +5 -1
- package/dist/esm/modules/excel/row.js +35 -2
- package/dist/esm/modules/excel/stream/workbook-writer.browser.js +22 -2
- package/dist/esm/modules/excel/utils/drawing-utils.js +19 -2
- package/dist/esm/modules/excel/workbook.browser.js +32 -2
- package/dist/esm/modules/excel/worksheet.js +83 -0
- package/dist/esm/modules/excel/xlsx/xform/comment/vml-shape-xform.js +42 -8
- package/dist/esm/modules/excel/xlsx/xform/core/content-types-xform.js +3 -1
- package/dist/esm/modules/excel/xlsx/xform/drawing/absolute-anchor-xform.js +5 -0
- package/dist/esm/modules/excel/xlsx/xform/drawing/base-cell-anchor-xform.js +18 -1
- package/dist/esm/modules/excel/xlsx/xform/drawing/blip-xform.js +38 -11
- package/dist/esm/modules/excel/xlsx/xform/drawing/one-cell-anchor-xform.js +5 -0
- package/dist/esm/modules/excel/xlsx/xform/drawing/pic-xform.js +2 -1
- package/dist/esm/modules/excel/xlsx/xform/drawing/shape-xform.js +109 -0
- package/dist/esm/modules/excel/xlsx/xform/drawing/two-cell-anchor-xform.js +10 -1
- package/dist/esm/modules/excel/xlsx/xform/sheet/worksheet-xform.js +64 -1
- package/dist/esm/modules/pdf/builder/document-builder.js +22 -49
- package/dist/esm/modules/pdf/builder/pdf-editor.js +1 -1
- package/dist/esm/modules/pdf/core/pdf-stream.js +38 -2
- package/dist/esm/modules/pdf/font/font-manager.js +35 -18
- package/dist/esm/modules/pdf/render/page-renderer.js +111 -18
- package/dist/esm/modules/word/advanced/field-engine.js +45 -20
- package/dist/esm/modules/word/advanced/glossary.js +8 -9
- package/dist/esm/modules/word/advanced/math-convert.js +94 -12
- package/dist/esm/modules/word/advanced/ole-objects.js +122 -19
- package/dist/esm/modules/word/advanced/style-map.js +31 -10
- package/dist/esm/modules/word/builder/run-builders.js +7 -1
- package/dist/esm/modules/word/constants.js +5 -1
- package/dist/esm/modules/word/convert/docx-to-semantic.js +135 -1
- package/dist/esm/modules/word/convert/html/html-import.js +167 -14
- package/dist/esm/modules/word/convert/html/html.js +1 -1
- package/dist/esm/modules/word/convert/markdown/markdown-import.js +279 -69
- package/dist/esm/modules/word/convert/odt/odt.js +407 -56
- package/dist/esm/modules/word/html.js +1 -1
- package/dist/esm/modules/word/index.base.js +1 -1
- package/dist/esm/modules/word/layout/layout-full.js +326 -19
- package/dist/esm/modules/word/layout/render-page.js +35 -8
- package/dist/esm/modules/word/query/compat.js +29 -21
- package/dist/esm/modules/word/reader/docx-reader.js +105 -2
- package/dist/esm/modules/word/reader/math-parser.js +8 -2
- package/dist/esm/modules/word/security/cfb-reader.js +5 -5
- package/dist/esm/modules/word/writer/docx-packager.js +108 -2
- package/dist/esm/modules/word/writer/glossary-writer.js +121 -0
- package/dist/esm/modules/word/writer/header-footer-writer.js +105 -20
- package/dist/esm/modules/word/writer/math-writer.js +7 -2
- package/dist/esm/utils/font-metrics.js +43 -0
- package/dist/esm/utils/theme-colors.js +4 -1
- package/dist/iife/excelts.iife.js +496 -59
- package/dist/iife/excelts.iife.js.map +1 -1
- package/dist/iife/excelts.iife.min.js +39 -39
- package/dist/types/modules/excel/cell.d.ts +4 -0
- package/dist/types/modules/excel/stream/workbook-writer.browser.d.ts +8 -1
- package/dist/types/modules/excel/types.d.ts +81 -0
- package/dist/types/modules/excel/utils/drawing-utils.d.ts +8 -0
- package/dist/types/modules/excel/workbook.browser.d.ts +16 -0
- package/dist/types/modules/excel/worksheet.d.ts +31 -1
- package/dist/types/modules/excel/xlsx/xform/comment/vml-shape-xform.d.ts +7 -0
- package/dist/types/modules/excel/xlsx/xform/drawing/blip-xform.d.ts +6 -0
- package/dist/types/modules/excel/xlsx/xform/drawing/one-cell-anchor-xform.d.ts +1 -0
- package/dist/types/modules/excel/xlsx/xform/drawing/pic-xform.d.ts +2 -0
- package/dist/types/modules/excel/xlsx/xform/drawing/shape-xform.d.ts +47 -0
- package/dist/types/modules/pdf/core/pdf-stream.d.ts +28 -1
- package/dist/types/modules/pdf/font/font-manager.d.ts +26 -0
- package/dist/types/modules/pdf/render/page-renderer.d.ts +51 -3
- package/dist/types/modules/word/advanced/glossary.d.ts +10 -36
- package/dist/types/modules/word/advanced/ole-objects.d.ts +28 -0
- package/dist/types/modules/word/builder/run-builders.d.ts +7 -1
- package/dist/types/modules/word/constants.d.ts +4 -0
- package/dist/types/modules/word/convert/docx-to-semantic.d.ts +2 -1
- package/dist/types/modules/word/convert/html/html-import.d.ts +32 -1
- package/dist/types/modules/word/convert/html/html.d.ts +2 -2
- package/dist/types/modules/word/convert/markdown/markdown-import.d.ts +48 -18
- package/dist/types/modules/word/convert/markdown/markdown.d.ts +1 -1
- package/dist/types/modules/word/html.d.ts +2 -2
- package/dist/types/modules/word/index.base.d.ts +3 -3
- package/dist/types/modules/word/markdown.d.ts +1 -1
- package/dist/types/modules/word/query/compat.d.ts +10 -2
- package/dist/types/modules/word/types.d.ts +96 -1
- package/dist/types/modules/word/writer/glossary-writer.d.ts +28 -0
- package/dist/types/utils/font-metrics.d.ts +8 -0
- package/package.json +3 -1
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
[](https://github.com/cjnoname/excelts/actions/workflows/ci.yml) [中文](README_zh.md)
|
|
4
4
|
|
|
5
|
-
The TypeScript ecosystem is heavily fragmented when it comes to document and data processing. To
|
|
5
|
+
The TypeScript ecosystem is heavily fragmented when it comes to document and data processing. To work with spreadsheets, documents, PDFs, and the many data and archive formats around them, developers often need to pull in a different package for each task — and then yet another set of packages to make them work in the browser, plus separate streaming wrappers on top. These libraries vary in API style, quality, and maintenance status, creating a tax on every project that needs more than one of them.
|
|
6
6
|
|
|
7
7
|
ExcelTS was built to fix this. One package, one consistent API, one codebase — working identically across Node.js, Bun, and browsers. Streaming is a first-class citizen in every module, not an afterthought bolted on through a third-party adapter. The goal is simple: install once, import what you need, and get the same reliable behavior everywhere — with maximum streaming performance out of the box.
|
|
8
8
|
|
|
@@ -12,13 +12,13 @@ ExcelTS is a zero-dependency TypeScript toolkit for spreadsheets and documents:
|
|
|
12
12
|
|
|
13
13
|
- **AI-Friendly** — Clean, consistent API designed for AI coding agents. Every module has comprehensive documentation and runnable examples for AI to learn from
|
|
14
14
|
- **Zero Runtime Dependencies** — Pure TypeScript, no external packages
|
|
15
|
-
- **
|
|
15
|
+
- **Nine Modules** — Excel, Word, Formula, PDF, CSV, Markdown, XML, Archive, Stream
|
|
16
16
|
- **Cross-Platform** — Node.js 22+, Bun, Chrome 89+, Firefox 102+, Safari 14.1+
|
|
17
17
|
- **ESM First** — Native ES Modules with CommonJS compatibility and full tree-shaking
|
|
18
18
|
|
|
19
19
|
## Modules
|
|
20
20
|
|
|
21
|
-
ExcelTS is organized into
|
|
21
|
+
ExcelTS is organized into nine standalone modules. Each module has its own documentation and runnable examples.
|
|
22
22
|
|
|
23
23
|
### Excel — XLSX/JSON Workbook Manager
|
|
24
24
|
|
|
@@ -27,6 +27,13 @@ Create, read, and modify Excel spreadsheets with full styling, formulas, images,
|
|
|
27
27
|
- [Documentation](src/modules/excel/README.md) | [中文](src/modules/excel/README_zh.md)
|
|
28
28
|
- [Examples](src/modules/excel/examples/)
|
|
29
29
|
|
|
30
|
+
### Word — DOCX Document Processor
|
|
31
|
+
|
|
32
|
+
Read, write, and manipulate DOCX files with a full builder, reader, and converter surface. Build documents with headings, tables, images, lists, headers/footers, drawing shapes, math, and charts. Read and modify existing files with text search/replace, format-aware queries, and bookmark/comment lookup. Convert to and from HTML and Markdown, bridge Excel workbooks into Word tables, and render Word straight to PDF. Advanced features include a template engine, form fields, OpenDoPE data binding, font embedding with subsetting, track-changes accept/reject, document diff/merge, streaming writer, password protection, Agile-encryption decryption, and digital-signature inspection.
|
|
33
|
+
|
|
34
|
+
- [Documentation](src/modules/word/README.md) | [中文](src/modules/word/README_zh.md)
|
|
35
|
+
- [Examples](src/modules/word/examples/)
|
|
36
|
+
|
|
30
37
|
### Formula — Excel-Compatible Calculation Engine
|
|
31
38
|
|
|
32
39
|
Standalone 433-function calculation engine with tokenizer, parser, dependency graph, dynamic-array spill, and `LAMBDA`/`LET`/`MAP`/`REDUCE` support. Ships as a separate subpath so it stays out of bundles that only need to read/write XLSX. **Works in two modes**: paired with `Workbook` via `installFormulaEngine()`, or standalone on any `WorkbookLike` host via `calculateFormulas()` — the engine itself has zero excel runtime dependencies.
|
|
@@ -150,6 +157,14 @@ const archive = await zip().add("hello.txt", "Hello!").bytes();
|
|
|
150
157
|
import { parseMarkdown, formatMarkdown } from "@cj-tech-master/excelts/markdown";
|
|
151
158
|
const table = parseMarkdown("| A | B |\n|---|---|\n| 1 | 2 |");
|
|
152
159
|
|
|
160
|
+
// Word — create, read, and convert DOCX
|
|
161
|
+
import { Document, toBuffer, readDocx } from "@cj-tech-master/excelts/word";
|
|
162
|
+
const wdoc = Document.create();
|
|
163
|
+
Document.addHeading(wdoc, "Report", 1);
|
|
164
|
+
Document.addParagraph(wdoc, "Generated by ExcelTS.");
|
|
165
|
+
const docxBytes = await toBuffer(Document.build(wdoc));
|
|
166
|
+
const parsedDocx = await readDocx(docxBytes); // round-trip read
|
|
167
|
+
|
|
153
168
|
// Formula — opt-in calculation engine (kept out of the base bundle)
|
|
154
169
|
//
|
|
155
170
|
// Mode A: paired with Workbook — enables wb.calculateFormulas()
|
package/README_zh.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
[](https://github.com/cjnoname/excelts/actions/workflows/ci.yml) [English](README.md)
|
|
4
4
|
|
|
5
|
-
TypeScript
|
|
5
|
+
TypeScript 生态在文档和数据处理领域长期存在碎片化问题。要处理电子表格、文档、PDF 以及围绕它们的各种数据和归档格式,往往需要分别引入不同的包;到了浏览器端又要换一套方案;流式处理还得再额外接入一个适配库。这些库的 API 风格、质量和维护状态参差不齐,给每个需要组合使用它们的项目都带来了额外的负担。
|
|
6
6
|
|
|
7
7
|
ExcelTS 正是为了解决这个问题而生。一个包、一套 API、一份代码 — 在 Node.js、Bun 和浏览器中行为完全一致。流式处理是每个模块的一等公民,而非通过第三方适配器后期拼装的附属品。目标很简单:安装一次,按需导入,在任何环境下都获得相同的可靠体验 — 同时将流式处理的性能发挥到极致。
|
|
8
8
|
|
|
@@ -12,13 +12,13 @@ ExcelTS 是一个零依赖的 TypeScript 电子表格和文档工具包:
|
|
|
12
12
|
|
|
13
13
|
- **AI 友好** — 简洁一致的 API,专为 AI 编程助手设计。每个模块都配有完整的文档和可运行的示例供 AI 学习
|
|
14
14
|
- **零运行时依赖** — 纯 TypeScript,无外部包
|
|
15
|
-
-
|
|
15
|
+
- **九大模块** — Excel、Word、Formula、PDF、CSV、Markdown、XML、Archive、Stream
|
|
16
16
|
- **跨平台** — Node.js 22+、Bun、Chrome 89+、Firefox 102+、Safari 14.1+
|
|
17
17
|
- **ESM 优先** — 原生 ES Modules,兼容 CommonJS,完整的 tree-shaking 支持
|
|
18
18
|
|
|
19
19
|
## 模块
|
|
20
20
|
|
|
21
|
-
ExcelTS
|
|
21
|
+
ExcelTS 由九个独立模块组成,每个模块都有自己的文档和可运行示例。
|
|
22
22
|
|
|
23
23
|
### Excel — XLSX/JSON 工作簿管理器
|
|
24
24
|
|
|
@@ -27,6 +27,13 @@ ExcelTS 由八个独立模块组成,每个模块都有自己的文档和可运
|
|
|
27
27
|
- [文档](src/modules/excel/README.md) | [中文](src/modules/excel/README_zh.md)
|
|
28
28
|
- [示例](src/modules/excel/examples/)
|
|
29
29
|
|
|
30
|
+
### Word — DOCX 文档处理器
|
|
31
|
+
|
|
32
|
+
读取、写入和操作 DOCX 文件,提供完整的构建器、读取器和转换器能力。可构建含标题、表格、图片、列表、页眉/页脚、绘图形状、数学公式和图表的文档;可对现有文件做文本查找/替换、格式感知查询、书签/批注查找等读取与修改;可与 HTML 和 Markdown 互相转换,将 Excel 工作簿桥接为 Word 表格,并将 Word 直接渲染为 PDF。高级功能包括模板引擎、表单字段、OpenDoPE 数据绑定、字体嵌入与子集化、修订追踪接受/拒绝、文档比对/合并、流式写入器、密码保护、Agile 加密解密以及数字签名检测。
|
|
33
|
+
|
|
34
|
+
- [文档](src/modules/word/README.md) | [中文](src/modules/word/README_zh.md)
|
|
35
|
+
- [示例](src/modules/word/examples/)
|
|
36
|
+
|
|
30
37
|
### Formula — Excel 兼容公式引擎
|
|
31
38
|
|
|
32
39
|
独立的 433 函数计算引擎,包含 tokenizer、parser、依赖图、动态数组 spill,支持 `LAMBDA`/`LET`/`MAP`/`REDUCE`。作为单独的 subpath 发布,不会被打进只读写 XLSX 的 bundle。**两种使用模式**:通过 `installFormulaEngine()` 和 `Workbook` 配合使用,或通过 `calculateFormulas()` 对任意 `WorkbookLike` 宿主单独使用 — 引擎本身**零 excel 运行时依赖**。
|
|
@@ -135,6 +142,14 @@ const archive = await zip().add("hello.txt", "Hello!").bytes();
|
|
|
135
142
|
import { parseMarkdown, formatMarkdown } from "@cj-tech-master/excelts/markdown";
|
|
136
143
|
const table = parseMarkdown("| A | B |\n|---|---|\n| 1 | 2 |");
|
|
137
144
|
|
|
145
|
+
// Word — 创建、读取和转换 DOCX
|
|
146
|
+
import { Document, toBuffer, readDocx } from "@cj-tech-master/excelts/word";
|
|
147
|
+
const wdoc = Document.create();
|
|
148
|
+
Document.addHeading(wdoc, "报告", 1);
|
|
149
|
+
Document.addParagraph(wdoc, "由 ExcelTS 生成。");
|
|
150
|
+
const docxBytes = await toBuffer(Document.build(wdoc));
|
|
151
|
+
const parsedDocx = await readDocx(docxBytes); // 往返读取
|
|
152
|
+
|
|
138
153
|
// Formula — 可选的公式引擎(默认不打进主 bundle)
|
|
139
154
|
//
|
|
140
155
|
// 模式 A: 配合 Workbook — 启用 wb.calculateFormulas()
|
|
@@ -43,6 +43,10 @@ export interface NoteConfig {
|
|
|
43
43
|
};
|
|
44
44
|
editAs?: string;
|
|
45
45
|
anchor?: string;
|
|
46
|
+
/** Comment box width in points. Defaults to 97.8pt when omitted. */
|
|
47
|
+
width?: number;
|
|
48
|
+
/** Comment box height in points. Defaults to 59.1pt when omitted. */
|
|
49
|
+
height?: number;
|
|
46
50
|
}
|
|
47
51
|
export interface NoteModel {
|
|
48
52
|
type: string;
|
|
@@ -36,7 +36,11 @@ class Note {
|
|
|
36
36
|
set model(value) {
|
|
37
37
|
const { note } = value;
|
|
38
38
|
const { texts } = note;
|
|
39
|
-
|
|
39
|
+
// A single, plain text run with no extra box geometry can be flattened
|
|
40
|
+
// back to a simple string. Custom width/height must keep the full config
|
|
41
|
+
// so the sizing survives the model round-trip.
|
|
42
|
+
const hasCustomSize = note.width !== undefined || note.height !== undefined;
|
|
43
|
+
if (texts && texts.length === 1 && Object.keys(texts[0]).length === 1 && !hasCustomSize) {
|
|
40
44
|
this.note = texts[0].text;
|
|
41
45
|
}
|
|
42
46
|
else {
|
|
@@ -213,12 +213,13 @@ class Row {
|
|
|
213
213
|
// (e.g. DB entities). Cell.value setter handles unknown values via Value.getType
|
|
214
214
|
// fallback to JSON type, so this cast is safe at runtime.
|
|
215
215
|
this._worksheet.eachColumnKey((column, key) => {
|
|
216
|
-
|
|
216
|
+
const resolved = resolveColumnKeyValue(value, key);
|
|
217
|
+
if (resolved !== undefined) {
|
|
217
218
|
this.getCellEx({
|
|
218
219
|
address: colCache.encodeAddress(this._number, column.number),
|
|
219
220
|
row: this._number,
|
|
220
221
|
col: column.number
|
|
221
|
-
}).value =
|
|
222
|
+
}).value = resolved;
|
|
222
223
|
}
|
|
223
224
|
});
|
|
224
225
|
}
|
|
@@ -459,4 +460,36 @@ class Row {
|
|
|
459
460
|
this.style = value.style ? structuredClone(value.style) : {};
|
|
460
461
|
}
|
|
461
462
|
}
|
|
463
|
+
/**
|
|
464
|
+
* Resolve a column key against a row object, supporting dotted nested paths.
|
|
465
|
+
*
|
|
466
|
+
* A key without a `.` takes the original fast path (`obj[key]`), preserving
|
|
467
|
+
* exact backward compatibility — including keys that legitimately contain a
|
|
468
|
+
* dot only as a flat property name, which still resolve via the fast path
|
|
469
|
+
* first. A dotted key (e.g. `"address.city"`) is resolved by walking each
|
|
470
|
+
* segment; if any segment is missing or not an object, the result is
|
|
471
|
+
* `undefined` (the same signal the caller already uses to skip a cell).
|
|
472
|
+
*
|
|
473
|
+
* @param obj - The row object supplied to `row.values = {...}` / `addRow({...})`.
|
|
474
|
+
* @param key - The column key, optionally a dotted path.
|
|
475
|
+
* @returns The resolved value, or `undefined` when the path cannot be followed.
|
|
476
|
+
*/
|
|
477
|
+
function resolveColumnKeyValue(obj, key) {
|
|
478
|
+
// Fast path: a flat key (no dot) or an exact flat property match. This keeps
|
|
479
|
+
// existing behaviour identical and also lets a literal "a.b" property win
|
|
480
|
+
// over nested traversal when it is actually present on the object.
|
|
481
|
+
const direct = obj[key];
|
|
482
|
+
if (direct !== undefined || !key.includes(".")) {
|
|
483
|
+
return direct;
|
|
484
|
+
}
|
|
485
|
+
// Dotted path: walk segments, bailing out to undefined on any gap.
|
|
486
|
+
let current = obj;
|
|
487
|
+
for (const segment of key.split(".")) {
|
|
488
|
+
if (current === null || typeof current !== "object") {
|
|
489
|
+
return undefined;
|
|
490
|
+
}
|
|
491
|
+
current = current[segment];
|
|
492
|
+
}
|
|
493
|
+
return current;
|
|
494
|
+
}
|
|
462
495
|
export { Row };
|
|
@@ -20,9 +20,16 @@ import type { Writable } from "../../stream/index.js";
|
|
|
20
20
|
* Extends the public {@link ImageData} shape with the unique stored name
|
|
21
21
|
* (`name`) assigned by `addImage`, and pins `type` to `"image"`.
|
|
22
22
|
*/
|
|
23
|
-
export interface Medium extends ImageData {
|
|
23
|
+
export interface Medium extends Omit<ImageData, "extension"> {
|
|
24
24
|
type: "image";
|
|
25
25
|
name: string;
|
|
26
|
+
/**
|
|
27
|
+
* Widened from `ImageData.extension` so an SVG companion medium can carry
|
|
28
|
+
* the `"svg"` extension (the public `addImage` input stays raster-only).
|
|
29
|
+
*/
|
|
30
|
+
extension: string;
|
|
31
|
+
/** Media index of an SVG companion (raster blip + svgBlip extension). */
|
|
32
|
+
svgMediaId?: number;
|
|
26
33
|
}
|
|
27
34
|
interface CommentRef {
|
|
28
35
|
commentName: string;
|
|
@@ -387,13 +387,33 @@ export class WorkbookWriterBase {
|
|
|
387
387
|
* ```
|
|
388
388
|
*/
|
|
389
389
|
addImage(image) {
|
|
390
|
+
const { svg, ...raster } = image;
|
|
391
|
+
if (svg &&
|
|
392
|
+
raster.link &&
|
|
393
|
+
raster.buffer == null &&
|
|
394
|
+
raster.base64 == null &&
|
|
395
|
+
raster.filename == null) {
|
|
396
|
+
throw new ImageError("An SVG image requires an embedded raster fallback (buffer/base64/filename); it cannot be combined with an external link.");
|
|
397
|
+
}
|
|
390
398
|
const id = this.media.length;
|
|
391
399
|
const medium = {
|
|
392
|
-
...
|
|
400
|
+
...raster,
|
|
393
401
|
type: "image",
|
|
394
|
-
name: `image${id}.${
|
|
402
|
+
name: `image${id}.${raster.extension}`
|
|
395
403
|
};
|
|
396
404
|
this.media.push(medium);
|
|
405
|
+
if (svg) {
|
|
406
|
+
// Register the SVG companion as a second image medium and link it back to
|
|
407
|
+
// the raster blip so the drawing serializer emits the svgBlip extension.
|
|
408
|
+
const svgId = this.media.length;
|
|
409
|
+
this.media.push({
|
|
410
|
+
...svg,
|
|
411
|
+
type: "image",
|
|
412
|
+
extension: "svg",
|
|
413
|
+
name: `image${svgId}.svg`
|
|
414
|
+
});
|
|
415
|
+
medium.svgMediaId = svgId;
|
|
416
|
+
}
|
|
397
417
|
return id;
|
|
398
418
|
}
|
|
399
419
|
getImage(id) {
|
|
@@ -497,6 +497,24 @@ export interface ImageData {
|
|
|
497
497
|
* are required.
|
|
498
498
|
*/
|
|
499
499
|
link?: string;
|
|
500
|
+
/**
|
|
501
|
+
* Attach a scalable SVG alongside a raster fallback.
|
|
502
|
+
*
|
|
503
|
+
* Excel stores SVG pictures as a raster `a:blip` (the `extension`/`buffer`/
|
|
504
|
+
* `base64`/`filename` on this object — typically a PNG) plus an
|
|
505
|
+
* `asvg:svgBlip` extension pointing at the vector data. The raster image is
|
|
506
|
+
* what older Excel versions and non-SVG consumers render, so it is required;
|
|
507
|
+
* modern Excel renders the crisp SVG. This library does **not** rasterize —
|
|
508
|
+
* you supply both the SVG bytes and the raster fallback you want embedded.
|
|
509
|
+
*/
|
|
510
|
+
svg?: {
|
|
511
|
+
/** SVG bytes (mutually use one of buffer/base64/filename). */
|
|
512
|
+
buffer?: Buffer;
|
|
513
|
+
/** Base64-encoded SVG. */
|
|
514
|
+
base64?: string;
|
|
515
|
+
/** Path to an `.svg` file (Node only). */
|
|
516
|
+
filename?: string;
|
|
517
|
+
};
|
|
500
518
|
}
|
|
501
519
|
export interface ImagePosition {
|
|
502
520
|
tl: {
|
|
@@ -556,6 +574,69 @@ export interface ImageHyperlinkValue {
|
|
|
556
574
|
hyperlink: string;
|
|
557
575
|
tooltip?: string;
|
|
558
576
|
}
|
|
577
|
+
/**
|
|
578
|
+
* Preset geometry for a drawing shape. Mirrors the OOXML `prst` vocabulary;
|
|
579
|
+
* the most common presets are surfaced here, but any valid preset name is
|
|
580
|
+
* accepted as a fallback `string`.
|
|
581
|
+
*/
|
|
582
|
+
export type ShapeType = "rect" | "roundRect" | "ellipse" | "triangle" | "line" | "rightArrow" | "leftArrow" | "upArrow" | "downArrow" | "diamond" | "hexagon" | "star5" | (string & {});
|
|
583
|
+
/** Options for `Worksheet.addShape`. */
|
|
584
|
+
export interface AddShapeOptions {
|
|
585
|
+
/** Preset geometry (defaults to `"rect"`). */
|
|
586
|
+
type?: ShapeType;
|
|
587
|
+
/** Where the shape sits — a cell range (e.g. `"B2:D5"`) or anchor object. */
|
|
588
|
+
range: AddImageRange;
|
|
589
|
+
/** Solid fill colour as hex RGB (e.g. `"FF0000"`). Omit for no fill. */
|
|
590
|
+
fillColor?: string;
|
|
591
|
+
/** Outline colour as hex RGB (e.g. `"000000"`). */
|
|
592
|
+
lineColor?: string;
|
|
593
|
+
/** Outline width in points. */
|
|
594
|
+
lineWidth?: number;
|
|
595
|
+
/** Optional centred text label. */
|
|
596
|
+
text?: string;
|
|
597
|
+
/** Display name (defaults to `"Shape N"`). */
|
|
598
|
+
name?: string;
|
|
599
|
+
}
|
|
600
|
+
/** Internal serialized model for a worksheet shape. */
|
|
601
|
+
export interface ShapeModel {
|
|
602
|
+
type: "shape";
|
|
603
|
+
shapeType: string;
|
|
604
|
+
range: AddImageRange;
|
|
605
|
+
fillColor?: string;
|
|
606
|
+
lineColor?: string;
|
|
607
|
+
lineWidth?: number;
|
|
608
|
+
text?: string;
|
|
609
|
+
name?: string;
|
|
610
|
+
/**
|
|
611
|
+
* Resolved anchor coordinates, filled in by the worksheet model getter so
|
|
612
|
+
* the serializer doesn't need range-parsing logic. Mirrors the three image
|
|
613
|
+
* anchoring modes: two-cell (`tl`+`br`), one-cell (`tl`+`ext`) and absolute
|
|
614
|
+
* (`pos`+`ext`). Internal only.
|
|
615
|
+
*/
|
|
616
|
+
anchorRange?: {
|
|
617
|
+
tl: {
|
|
618
|
+
nativeCol: number;
|
|
619
|
+
nativeColOff: number;
|
|
620
|
+
nativeRow: number;
|
|
621
|
+
nativeRowOff: number;
|
|
622
|
+
};
|
|
623
|
+
br?: {
|
|
624
|
+
nativeCol: number;
|
|
625
|
+
nativeColOff: number;
|
|
626
|
+
nativeRow: number;
|
|
627
|
+
nativeRowOff: number;
|
|
628
|
+
};
|
|
629
|
+
ext?: {
|
|
630
|
+
width?: number;
|
|
631
|
+
height?: number;
|
|
632
|
+
};
|
|
633
|
+
pos?: {
|
|
634
|
+
x: number;
|
|
635
|
+
y: number;
|
|
636
|
+
};
|
|
637
|
+
editAs?: string;
|
|
638
|
+
};
|
|
639
|
+
}
|
|
559
640
|
/**
|
|
560
641
|
* Watermark placement mode in the Excel worksheet.
|
|
561
642
|
*
|
|
@@ -19,6 +19,12 @@ export interface DrawingAnchor {
|
|
|
19
19
|
* (`<a:blip r:link>`) instead of an embedded one (`<a:blip r:embed>`).
|
|
20
20
|
*/
|
|
21
21
|
external?: boolean;
|
|
22
|
+
/**
|
|
23
|
+
* Relationship id of an SVG companion. When set, the raster `a:blip`
|
|
24
|
+
* (referenced by `rId`) carries an `asvg:svgBlip` extension pointing at
|
|
25
|
+
* the SVG media via this id.
|
|
26
|
+
*/
|
|
27
|
+
svgRId?: string;
|
|
22
28
|
};
|
|
23
29
|
range: any;
|
|
24
30
|
}
|
|
@@ -54,6 +60,8 @@ export interface MediaLike {
|
|
|
54
60
|
buffer?: unknown;
|
|
55
61
|
base64?: unknown;
|
|
56
62
|
filename?: unknown;
|
|
63
|
+
/** Media index of an SVG companion (raster blip + svgBlip extension). */
|
|
64
|
+
svgMediaId?: number;
|
|
57
65
|
}
|
|
58
66
|
/**
|
|
59
67
|
* Resolves a media filename into the drawing-level relative target path.
|
|
@@ -122,6 +122,23 @@ export function buildDrawingAnchorsAndRels(media, existingRels, options) {
|
|
|
122
122
|
},
|
|
123
123
|
range: medium.range
|
|
124
124
|
};
|
|
125
|
+
// SVG companion: allocate (and dedupe) a rel for the vector media, then
|
|
126
|
+
// record its rId so the blip serializer emits the asvg:svgBlip extension.
|
|
127
|
+
if (bookImage.svgMediaId !== undefined) {
|
|
128
|
+
const svgKey = `svg:${bookImage.svgMediaId}`;
|
|
129
|
+
let rIdSvg = imageRIdMap[svgKey];
|
|
130
|
+
if (!rIdSvg) {
|
|
131
|
+
const svgImage = options.getBookImage(bookImage.svgMediaId);
|
|
132
|
+
if (svgImage) {
|
|
133
|
+
rIdSvg = options.nextRId(rels);
|
|
134
|
+
imageRIdMap[svgKey] = rIdSvg;
|
|
135
|
+
rels.push(buildImageRel(rIdSvg, svgImage));
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
if (rIdSvg) {
|
|
139
|
+
anchor.picture.svgRId = rIdSvg;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
125
142
|
// Pass through watermark opacity as alphaModFix
|
|
126
143
|
if (medium.opacity !== undefined) {
|
|
127
144
|
const clamped = Math.max(0, Math.min(1, medium.opacity));
|
|
@@ -172,8 +189,8 @@ export function filterDrawingAnchors(anchors) {
|
|
|
172
189
|
if (a.range?.br && a.shape) {
|
|
173
190
|
return true;
|
|
174
191
|
}
|
|
175
|
-
// One-cell anchors need a valid picture
|
|
176
|
-
if (!a.range?.br && !a.picture && !a.graphicFrame) {
|
|
192
|
+
// One-cell anchors need a valid picture, graphicFrame (charts) or shape.
|
|
193
|
+
if (!a.range?.br && !a.picture && !a.graphicFrame && !a.shape) {
|
|
177
194
|
return false;
|
|
178
195
|
}
|
|
179
196
|
// Two-cell anchors need either picture, shape, or graphicFrame (charts)
|
|
@@ -34,6 +34,12 @@ export interface WorkbookMedia {
|
|
|
34
34
|
name?: string;
|
|
35
35
|
/** External link target — when set, the image is referenced, not embedded. */
|
|
36
36
|
link?: string;
|
|
37
|
+
/**
|
|
38
|
+
* Media index of the SVG companion for this raster image. When set, the
|
|
39
|
+
* picture is written as a raster `a:blip` plus an `asvg:svgBlip` extension
|
|
40
|
+
* referencing the SVG media at this index. Internal bookkeeping only.
|
|
41
|
+
*/
|
|
42
|
+
svgMediaId?: number;
|
|
37
43
|
}
|
|
38
44
|
/** Internal model type for serialization */
|
|
39
45
|
export interface WorkbookModel {
|
|
@@ -820,6 +826,16 @@ declare class Workbook {
|
|
|
820
826
|
* const id = workbook.addImage({ extension: "png", link: "https://example.com/logo.png" });
|
|
821
827
|
* worksheet.addImage(id, "B2:D6");
|
|
822
828
|
* ```
|
|
829
|
+
*
|
|
830
|
+
* @example SVG with raster fallback — crisp in modern Excel, safe everywhere
|
|
831
|
+
* ```typescript
|
|
832
|
+
* const id = workbook.addImage({
|
|
833
|
+
* buffer: pngFallbackBytes, // shown by older Excel / non-SVG consumers
|
|
834
|
+
* extension: "png",
|
|
835
|
+
* svg: { buffer: svgBytes } // shown by Excel 2016+
|
|
836
|
+
* });
|
|
837
|
+
* worksheet.addImage(id, "B2:D6");
|
|
838
|
+
* ```
|
|
823
839
|
*/
|
|
824
840
|
addImage(image: ImageData): number;
|
|
825
841
|
getImage(id: number | string): WorkbookMedia | undefined;
|
|
@@ -20,7 +20,7 @@ import { parseNumberFromCsv } from "../csv/utils/number.js";
|
|
|
20
20
|
import { getChartSupport } from "./chart-host-registry.js";
|
|
21
21
|
import { Chartsheet } from "./chartsheet.js";
|
|
22
22
|
import { DefinedNames } from "./defined-names.js";
|
|
23
|
-
import { ExcelDownloadError, ExcelNotSupportedError, WorksheetNameError } from "./errors.js";
|
|
23
|
+
import { ExcelDownloadError, ExcelNotSupportedError, ImageError, WorksheetNameError } from "./errors.js";
|
|
24
24
|
import { withPivotChartSource } from "./pivot-chart.js";
|
|
25
25
|
import { WorkbookReader } from "./stream/workbook-reader.browser.js";
|
|
26
26
|
import { WorkbookWriter } from "./stream/workbook-writer.browser.js";
|
|
@@ -1733,10 +1733,40 @@ class Workbook {
|
|
|
1733
1733
|
* const id = workbook.addImage({ extension: "png", link: "https://example.com/logo.png" });
|
|
1734
1734
|
* worksheet.addImage(id, "B2:D6");
|
|
1735
1735
|
* ```
|
|
1736
|
+
*
|
|
1737
|
+
* @example SVG with raster fallback — crisp in modern Excel, safe everywhere
|
|
1738
|
+
* ```typescript
|
|
1739
|
+
* const id = workbook.addImage({
|
|
1740
|
+
* buffer: pngFallbackBytes, // shown by older Excel / non-SVG consumers
|
|
1741
|
+
* extension: "png",
|
|
1742
|
+
* svg: { buffer: svgBytes } // shown by Excel 2016+
|
|
1743
|
+
* });
|
|
1744
|
+
* worksheet.addImage(id, "B2:D6");
|
|
1745
|
+
* ```
|
|
1736
1746
|
*/
|
|
1737
1747
|
addImage(image) {
|
|
1748
|
+
const { svg, ...raster } = image;
|
|
1749
|
+
if (svg &&
|
|
1750
|
+
raster.link &&
|
|
1751
|
+
raster.buffer == null &&
|
|
1752
|
+
raster.base64 == null &&
|
|
1753
|
+
raster.filename == null) {
|
|
1754
|
+
// An SVG companion needs an embedded raster fallback; a *linked* (external)
|
|
1755
|
+
// raster has no package part to attach the svgBlip extension to.
|
|
1756
|
+
throw new ImageError("An SVG image requires an embedded raster fallback (buffer/base64/filename); it cannot be combined with an external link.");
|
|
1757
|
+
}
|
|
1738
1758
|
const id = this.media.length;
|
|
1739
|
-
|
|
1759
|
+
const rasterMedia = { ...raster, type: "image" };
|
|
1760
|
+
this.media.push(rasterMedia);
|
|
1761
|
+
if (svg) {
|
|
1762
|
+
// Register the SVG as a second `type: "image"` media so it flows through
|
|
1763
|
+
// the existing media naming, content-types, and zip-writing paths. Link
|
|
1764
|
+
// it back to the raster blip so the drawing serializer can emit the
|
|
1765
|
+
// asvg:svgBlip extension.
|
|
1766
|
+
const svgId = this.media.length;
|
|
1767
|
+
this.media.push({ ...svg, type: "image", extension: "svg" });
|
|
1768
|
+
rasterMedia.svgMediaId = svgId;
|
|
1769
|
+
}
|
|
1740
1770
|
return id;
|
|
1741
1771
|
}
|
|
1742
1772
|
getImage(id) {
|
|
@@ -14,7 +14,7 @@ import { Range, type RangeInput } from "./range.js";
|
|
|
14
14
|
import { Row, type RowModel } from "./row.js";
|
|
15
15
|
import type { AddSparklineGroupOptions, SparklineGroup } from "./sparkline/index.js";
|
|
16
16
|
import { Table, type TableModel } from "./table.js";
|
|
17
|
-
import type { AddImageRange, AutoFilter, CellValue, ColBreak, ConditionalFormattingOptions, DataValidation, IgnoredError, RowBreak, RowValues, TableProperties, ThreadedComment, WatermarkOptions, WorksheetProperties, WorksheetState, WorksheetView } from "./types.js";
|
|
17
|
+
import type { AddImageRange, AddShapeOptions, AutoFilter, CellValue, ColBreak, ConditionalFormattingOptions, DataValidation, IgnoredError, RowBreak, RowValues, ShapeModel, TableProperties, ThreadedComment, WatermarkOptions, WorksheetProperties, WorksheetState, WorksheetView } from "./types.js";
|
|
18
18
|
import { type Origin } from "./utils/address.js";
|
|
19
19
|
import type { Workbook } from "./workbook.js";
|
|
20
20
|
type DataValidationModel = {
|
|
@@ -110,6 +110,7 @@ interface WorksheetModel {
|
|
|
110
110
|
views: Partial<WorksheetView>[];
|
|
111
111
|
autoFilter: AutoFilter | null;
|
|
112
112
|
media: ImageModel[];
|
|
113
|
+
shapes?: ShapeModel[];
|
|
113
114
|
sheetProtection: SheetProtection | null;
|
|
114
115
|
tables: TableModel[];
|
|
115
116
|
pivotTables: PivotTable[];
|
|
@@ -156,6 +157,7 @@ declare class Worksheet {
|
|
|
156
157
|
views: Partial<WorksheetView>[];
|
|
157
158
|
autoFilter: AutoFilter | null;
|
|
158
159
|
private _media;
|
|
160
|
+
private _shapes;
|
|
159
161
|
private _charts;
|
|
160
162
|
private _sparklineGroups;
|
|
161
163
|
sheetProtection: SheetProtection | null;
|
|
@@ -362,6 +364,34 @@ declare class Worksheet {
|
|
|
362
364
|
* embed an image within the worksheet to cover a range
|
|
363
365
|
*/
|
|
364
366
|
addImage(imageId: string | number, range: AddImageRange): void;
|
|
367
|
+
/**
|
|
368
|
+
* Add a free-form drawing shape (rectangle, ellipse, line, text box, …) to
|
|
369
|
+
* the worksheet, anchored to a cell range.
|
|
370
|
+
*
|
|
371
|
+
* Unlike images, shapes need no media file — the geometry, fill, outline and
|
|
372
|
+
* optional text label are written directly into the drawing part.
|
|
373
|
+
*
|
|
374
|
+
* @example
|
|
375
|
+
* ```typescript
|
|
376
|
+
* worksheet.addShape({
|
|
377
|
+
* type: "rect",
|
|
378
|
+
* range: "B2:D5",
|
|
379
|
+
* fillColor: "FFD966",
|
|
380
|
+
* lineColor: "000000",
|
|
381
|
+
* lineWidth: 1,
|
|
382
|
+
* text: "Important"
|
|
383
|
+
* });
|
|
384
|
+
* ```
|
|
385
|
+
*/
|
|
386
|
+
addShape(options: AddShapeOptions): void;
|
|
387
|
+
/** All shapes added to this worksheet. */
|
|
388
|
+
getShapes(): ShapeModel[];
|
|
389
|
+
/**
|
|
390
|
+
* Resolve a shape's `range` into concrete two-cell anchor coordinates,
|
|
391
|
+
* reusing the `Image` range parser so cell-address/anchor handling stays in
|
|
392
|
+
* one place. Returns a serializable ShapeModel for the worksheet xform.
|
|
393
|
+
*/
|
|
394
|
+
private _resolveShapeModel;
|
|
365
395
|
getImages(): Image[];
|
|
366
396
|
/**
|
|
367
397
|
* Add a chart to the worksheet, positioned at the given range.
|
|
@@ -105,6 +105,8 @@ class Worksheet {
|
|
|
105
105
|
this.autoFilter = options.autoFilter ?? null;
|
|
106
106
|
// for images, etc
|
|
107
107
|
this._media = [];
|
|
108
|
+
// for user-drawn shapes (rectangles, lines, text boxes, …)
|
|
109
|
+
this._shapes = [];
|
|
108
110
|
// for charts
|
|
109
111
|
this._charts = [];
|
|
110
112
|
this._sparklineGroups = [];
|
|
@@ -1009,6 +1011,85 @@ class Worksheet {
|
|
|
1009
1011
|
};
|
|
1010
1012
|
this._media.push(new Image(this, model));
|
|
1011
1013
|
}
|
|
1014
|
+
/**
|
|
1015
|
+
* Add a free-form drawing shape (rectangle, ellipse, line, text box, …) to
|
|
1016
|
+
* the worksheet, anchored to a cell range.
|
|
1017
|
+
*
|
|
1018
|
+
* Unlike images, shapes need no media file — the geometry, fill, outline and
|
|
1019
|
+
* optional text label are written directly into the drawing part.
|
|
1020
|
+
*
|
|
1021
|
+
* @example
|
|
1022
|
+
* ```typescript
|
|
1023
|
+
* worksheet.addShape({
|
|
1024
|
+
* type: "rect",
|
|
1025
|
+
* range: "B2:D5",
|
|
1026
|
+
* fillColor: "FFD966",
|
|
1027
|
+
* lineColor: "000000",
|
|
1028
|
+
* lineWidth: 1,
|
|
1029
|
+
* text: "Important"
|
|
1030
|
+
* });
|
|
1031
|
+
* ```
|
|
1032
|
+
*/
|
|
1033
|
+
addShape(options) {
|
|
1034
|
+
const range = options.range;
|
|
1035
|
+
// A shape must cover an area, mirroring images. Reject inputs that resolve
|
|
1036
|
+
// to no size up front, with a clear shape-specific message — otherwise the
|
|
1037
|
+
// failure surfaces much later as a confusing `ImageError` from the internal
|
|
1038
|
+
// range parser when the worksheet is serialized.
|
|
1039
|
+
const hasArea = (typeof range === "string" && range.includes(":")) ||
|
|
1040
|
+
(typeof range === "object" &&
|
|
1041
|
+
range !== null &&
|
|
1042
|
+
("br" in range || "ext" in range || "pos" in range));
|
|
1043
|
+
if (!hasArea) {
|
|
1044
|
+
throw new ImageError('addShape requires a range covering an area: a cell range like "B2:D5", or an object with `br`, `ext`, or `pos`.');
|
|
1045
|
+
}
|
|
1046
|
+
this._shapes.push({
|
|
1047
|
+
type: "shape",
|
|
1048
|
+
shapeType: options.type ?? "rect",
|
|
1049
|
+
range,
|
|
1050
|
+
fillColor: options.fillColor,
|
|
1051
|
+
lineColor: options.lineColor,
|
|
1052
|
+
lineWidth: options.lineWidth,
|
|
1053
|
+
text: options.text,
|
|
1054
|
+
name: options.name
|
|
1055
|
+
});
|
|
1056
|
+
}
|
|
1057
|
+
/** All shapes added to this worksheet. */
|
|
1058
|
+
getShapes() {
|
|
1059
|
+
return this._shapes.slice();
|
|
1060
|
+
}
|
|
1061
|
+
/**
|
|
1062
|
+
* Resolve a shape's `range` into concrete two-cell anchor coordinates,
|
|
1063
|
+
* reusing the `Image` range parser so cell-address/anchor handling stays in
|
|
1064
|
+
* one place. Returns a serializable ShapeModel for the worksheet xform.
|
|
1065
|
+
*/
|
|
1066
|
+
_resolveShapeModel(shape) {
|
|
1067
|
+
let range;
|
|
1068
|
+
try {
|
|
1069
|
+
const probe = new Image(this, { type: "image", imageId: "", range: shape.range });
|
|
1070
|
+
// The probe is always an "image" type, so its model carries `range`.
|
|
1071
|
+
range = probe.model.range;
|
|
1072
|
+
}
|
|
1073
|
+
catch {
|
|
1074
|
+
// Range could not be parsed into an anchor (addShape validates the common
|
|
1075
|
+
// cases up front; this guards exotic inputs). Drop the anchor so the
|
|
1076
|
+
// serializer skips this shape rather than failing the whole worksheet.
|
|
1077
|
+
range = undefined;
|
|
1078
|
+
}
|
|
1079
|
+
if (!range) {
|
|
1080
|
+
return { ...shape, anchorRange: undefined };
|
|
1081
|
+
}
|
|
1082
|
+
return {
|
|
1083
|
+
...shape,
|
|
1084
|
+
anchorRange: {
|
|
1085
|
+
tl: range.tl,
|
|
1086
|
+
br: range.br,
|
|
1087
|
+
ext: range.ext,
|
|
1088
|
+
pos: range.pos,
|
|
1089
|
+
editAs: range.editAs
|
|
1090
|
+
}
|
|
1091
|
+
};
|
|
1092
|
+
}
|
|
1012
1093
|
getImages() {
|
|
1013
1094
|
return this._media.filter(m => m.type === "image");
|
|
1014
1095
|
}
|
|
@@ -1754,6 +1835,7 @@ class Worksheet {
|
|
|
1754
1835
|
views: this.views,
|
|
1755
1836
|
autoFilter: this.autoFilter,
|
|
1756
1837
|
media: this._media.map(medium => medium.model),
|
|
1838
|
+
shapes: this._shapes.map(shape => this._resolveShapeModel(shape)),
|
|
1757
1839
|
sheetProtection: this.sheetProtection,
|
|
1758
1840
|
tables: Object.values(this.tables).map(table => table.model),
|
|
1759
1841
|
pivotTables: this.pivotTables,
|
|
@@ -1819,6 +1901,7 @@ class Worksheet {
|
|
|
1819
1901
|
this.views = value.views;
|
|
1820
1902
|
this.autoFilter = value.autoFilter;
|
|
1821
1903
|
this._media = value.media.map(medium => new Image(this, medium));
|
|
1904
|
+
this._shapes = value.shapes ? value.shapes.slice() : [];
|
|
1822
1905
|
// Restore watermark state from media entries
|
|
1823
1906
|
this._watermark = value.watermark ?? null;
|
|
1824
1907
|
if (!this._watermark) {
|