@cj-tech-master/excelts 6.1.3 → 6.2.0-canary.20260328172614.9d0f2b2

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.
Files changed (181) hide show
  1. package/README.md +124 -8
  2. package/README_zh.md +124 -8
  3. package/dist/browser/index.browser.d.ts +6 -5
  4. package/dist/browser/index.browser.js +8 -5
  5. package/dist/browser/index.d.ts +2 -0
  6. package/dist/browser/index.js +4 -0
  7. package/dist/browser/modules/archive/unzip/zip-extract-core.js +22 -0
  8. package/dist/browser/modules/archive/zip/zip-entry-options.d.ts +9 -9
  9. package/dist/browser/modules/archive/zip-spec/zip-parser-core.js +17 -5
  10. package/dist/browser/modules/csv/format/formatter.js +6 -1
  11. package/dist/browser/modules/csv/index.d.ts +2 -2
  12. package/dist/browser/modules/csv/index.js +3 -3
  13. package/dist/browser/modules/csv/parse/sync.js +6 -1
  14. package/dist/browser/modules/csv/worker/worker-script.generated.d.ts +1 -1
  15. package/dist/browser/modules/csv/worker/worker-script.generated.js +1 -1
  16. package/dist/browser/modules/excel/errors.d.ts +2 -2
  17. package/dist/browser/modules/excel/stream/workbook-reader.browser.d.ts +12 -0
  18. package/dist/browser/modules/excel/stream/workbook-reader.browser.js +16 -2
  19. package/dist/browser/modules/excel/stream/workbook-reader.js +18 -0
  20. package/dist/browser/modules/excel/stream/worksheet-writer.js +5 -24
  21. package/dist/browser/modules/excel/utils/cell-format.d.ts +17 -0
  22. package/dist/browser/modules/excel/utils/cell-format.js +38 -0
  23. package/dist/browser/modules/excel/utils/font-data.d.ts +90 -0
  24. package/dist/browser/modules/excel/utils/font-data.js +719 -0
  25. package/dist/browser/modules/excel/utils/sheet-protection.d.ts +26 -0
  26. package/dist/browser/modules/excel/utils/sheet-protection.js +35 -0
  27. package/dist/browser/modules/excel/utils/text-metrics.d.ts +167 -0
  28. package/dist/browser/modules/excel/utils/text-metrics.js +556 -0
  29. package/dist/browser/modules/excel/workbook.browser.js +3 -1
  30. package/dist/browser/modules/excel/worksheet.d.ts +34 -0
  31. package/dist/browser/modules/excel/worksheet.js +197 -60
  32. package/dist/browser/modules/excel/xlsx/xform/sheet/cf-ext/conditional-formatting-ext-xform.d.ts +1 -1
  33. package/dist/browser/modules/excel/xlsx/xform/sheet/cf-ext/conditional-formattings-ext-xform.d.ts +1 -1
  34. package/dist/browser/modules/excel/xlsx/xform/sheet/cf-ext/databar-ext-xform.d.ts +1 -1
  35. package/dist/browser/modules/excel/xlsx/xform/sheet/cf-ext/icon-set-ext-xform.d.ts +1 -1
  36. package/dist/browser/modules/excel/xlsx/xform/sheet/worksheet-xform.js +66 -33
  37. package/dist/browser/modules/pdf/core/encryption.d.ts +77 -0
  38. package/dist/browser/modules/pdf/core/encryption.js +310 -0
  39. package/dist/browser/modules/pdf/core/pdf-object.d.ts +65 -0
  40. package/dist/browser/modules/pdf/core/pdf-object.js +180 -0
  41. package/dist/browser/modules/pdf/core/pdf-stream.d.ts +195 -0
  42. package/dist/browser/modules/pdf/core/pdf-stream.js +456 -0
  43. package/dist/browser/modules/pdf/core/pdf-writer.d.ts +84 -0
  44. package/dist/browser/modules/pdf/core/pdf-writer.js +452 -0
  45. package/dist/browser/modules/pdf/errors.d.ts +33 -0
  46. package/dist/browser/modules/pdf/errors.js +46 -0
  47. package/dist/browser/modules/pdf/excel-bridge.d.ts +29 -0
  48. package/dist/browser/modules/pdf/excel-bridge.js +423 -0
  49. package/dist/browser/modules/pdf/font/font-embedder.d.ts +49 -0
  50. package/dist/browser/modules/pdf/font/font-embedder.js +559 -0
  51. package/dist/browser/modules/pdf/font/font-manager.d.ts +119 -0
  52. package/dist/browser/modules/pdf/font/font-manager.js +357 -0
  53. package/dist/browser/modules/pdf/font/metrics.d.ts +59 -0
  54. package/dist/browser/modules/pdf/font/metrics.js +244 -0
  55. package/dist/browser/modules/pdf/font/ttf-parser.d.ts +73 -0
  56. package/dist/browser/modules/pdf/font/ttf-parser.js +531 -0
  57. package/dist/browser/modules/pdf/index.d.ts +37 -0
  58. package/dist/browser/modules/pdf/index.js +41 -0
  59. package/dist/browser/modules/pdf/pdf.d.ts +121 -0
  60. package/dist/browser/modules/pdf/pdf.js +255 -0
  61. package/dist/browser/modules/pdf/render/layout-engine.d.ts +25 -0
  62. package/dist/browser/modules/pdf/render/layout-engine.js +649 -0
  63. package/dist/browser/modules/pdf/render/page-renderer.d.ts +42 -0
  64. package/dist/browser/modules/pdf/render/page-renderer.js +637 -0
  65. package/dist/browser/modules/pdf/render/pdf-exporter.d.ts +19 -0
  66. package/dist/browser/modules/pdf/render/pdf-exporter.js +484 -0
  67. package/dist/browser/modules/pdf/render/png-decoder.d.ts +26 -0
  68. package/dist/browser/modules/pdf/render/png-decoder.js +295 -0
  69. package/dist/browser/modules/pdf/render/style-converter.d.ts +67 -0
  70. package/dist/browser/modules/pdf/render/style-converter.js +308 -0
  71. package/dist/browser/modules/pdf/types.d.ts +510 -0
  72. package/dist/browser/modules/pdf/types.js +36 -0
  73. package/dist/cjs/index.js +14 -2
  74. package/dist/cjs/modules/archive/unzip/zip-extract-core.js +22 -0
  75. package/dist/cjs/modules/archive/zip-spec/zip-parser-core.js +17 -5
  76. package/dist/cjs/modules/csv/format/formatter.js +6 -1
  77. package/dist/cjs/modules/csv/index.js +14 -14
  78. package/dist/cjs/modules/csv/parse/sync.js +6 -1
  79. package/dist/cjs/modules/csv/worker/worker-script.generated.js +1 -1
  80. package/dist/cjs/modules/excel/stream/workbook-reader.browser.js +16 -2
  81. package/dist/cjs/modules/excel/stream/workbook-reader.js +18 -0
  82. package/dist/cjs/modules/excel/stream/worksheet-writer.js +5 -24
  83. package/dist/cjs/modules/excel/utils/cell-format.js +39 -0
  84. package/dist/cjs/modules/excel/utils/font-data.js +729 -0
  85. package/dist/cjs/modules/excel/utils/sheet-protection.js +38 -0
  86. package/dist/cjs/modules/excel/utils/text-metrics.js +574 -0
  87. package/dist/cjs/modules/excel/workbook.browser.js +3 -1
  88. package/dist/cjs/modules/excel/worksheet.js +196 -59
  89. package/dist/cjs/modules/excel/xlsx/xform/sheet/worksheet-xform.js +66 -33
  90. package/dist/cjs/modules/pdf/core/encryption.js +316 -0
  91. package/dist/cjs/modules/pdf/core/pdf-object.js +192 -0
  92. package/dist/cjs/modules/pdf/core/pdf-stream.js +460 -0
  93. package/dist/cjs/modules/pdf/core/pdf-writer.js +456 -0
  94. package/dist/cjs/modules/pdf/errors.js +54 -0
  95. package/dist/cjs/modules/pdf/excel-bridge.js +426 -0
  96. package/dist/cjs/modules/pdf/font/font-embedder.js +562 -0
  97. package/dist/cjs/modules/pdf/font/font-manager.js +362 -0
  98. package/dist/cjs/modules/pdf/font/metrics.js +253 -0
  99. package/dist/cjs/modules/pdf/font/ttf-parser.js +534 -0
  100. package/dist/cjs/modules/pdf/index.js +52 -0
  101. package/dist/cjs/modules/pdf/pdf.js +258 -0
  102. package/dist/cjs/modules/pdf/render/layout-engine.js +653 -0
  103. package/dist/cjs/modules/pdf/render/page-renderer.js +644 -0
  104. package/dist/cjs/modules/pdf/render/pdf-exporter.js +487 -0
  105. package/dist/cjs/modules/pdf/render/png-decoder.js +298 -0
  106. package/dist/cjs/modules/pdf/render/style-converter.js +319 -0
  107. package/dist/cjs/modules/pdf/types.js +39 -0
  108. package/dist/esm/index.browser.js +8 -5
  109. package/dist/esm/index.js +4 -0
  110. package/dist/esm/modules/archive/unzip/zip-extract-core.js +22 -0
  111. package/dist/esm/modules/archive/zip-spec/zip-parser-core.js +17 -5
  112. package/dist/esm/modules/csv/format/formatter.js +6 -1
  113. package/dist/esm/modules/csv/index.js +3 -3
  114. package/dist/esm/modules/csv/parse/sync.js +6 -1
  115. package/dist/esm/modules/csv/worker/worker-script.generated.js +1 -1
  116. package/dist/esm/modules/excel/stream/workbook-reader.browser.js +16 -2
  117. package/dist/esm/modules/excel/stream/workbook-reader.js +18 -0
  118. package/dist/esm/modules/excel/stream/worksheet-writer.js +5 -24
  119. package/dist/esm/modules/excel/utils/cell-format.js +38 -0
  120. package/dist/esm/modules/excel/utils/font-data.js +719 -0
  121. package/dist/esm/modules/excel/utils/sheet-protection.js +35 -0
  122. package/dist/esm/modules/excel/utils/text-metrics.js +556 -0
  123. package/dist/esm/modules/excel/workbook.browser.js +3 -1
  124. package/dist/esm/modules/excel/worksheet.js +197 -60
  125. package/dist/esm/modules/excel/xlsx/xform/sheet/worksheet-xform.js +66 -33
  126. package/dist/esm/modules/pdf/core/encryption.js +310 -0
  127. package/dist/esm/modules/pdf/core/pdf-object.js +180 -0
  128. package/dist/esm/modules/pdf/core/pdf-stream.js +456 -0
  129. package/dist/esm/modules/pdf/core/pdf-writer.js +452 -0
  130. package/dist/esm/modules/pdf/errors.js +46 -0
  131. package/dist/esm/modules/pdf/excel-bridge.js +423 -0
  132. package/dist/esm/modules/pdf/font/font-embedder.js +559 -0
  133. package/dist/esm/modules/pdf/font/font-manager.js +357 -0
  134. package/dist/esm/modules/pdf/font/metrics.js +244 -0
  135. package/dist/esm/modules/pdf/font/ttf-parser.js +531 -0
  136. package/dist/esm/modules/pdf/index.js +41 -0
  137. package/dist/esm/modules/pdf/pdf.js +255 -0
  138. package/dist/esm/modules/pdf/render/layout-engine.js +649 -0
  139. package/dist/esm/modules/pdf/render/page-renderer.js +637 -0
  140. package/dist/esm/modules/pdf/render/pdf-exporter.js +484 -0
  141. package/dist/esm/modules/pdf/render/png-decoder.js +295 -0
  142. package/dist/esm/modules/pdf/render/style-converter.js +308 -0
  143. package/dist/esm/modules/pdf/types.js +36 -0
  144. package/dist/iife/excelts.iife.js +12754 -2803
  145. package/dist/iife/excelts.iife.js.map +1 -1
  146. package/dist/iife/excelts.iife.min.js +56 -34
  147. package/dist/types/index.browser.d.ts +6 -5
  148. package/dist/types/index.d.ts +2 -0
  149. package/dist/types/modules/archive/zip/zip-entry-options.d.ts +9 -9
  150. package/dist/types/modules/csv/index.d.ts +2 -2
  151. package/dist/types/modules/csv/worker/worker-script.generated.d.ts +1 -1
  152. package/dist/types/modules/excel/errors.d.ts +2 -2
  153. package/dist/types/modules/excel/stream/workbook-reader.browser.d.ts +12 -0
  154. package/dist/types/modules/excel/utils/cell-format.d.ts +17 -0
  155. package/dist/types/modules/excel/utils/font-data.d.ts +90 -0
  156. package/dist/types/modules/excel/utils/sheet-protection.d.ts +26 -0
  157. package/dist/types/modules/excel/utils/text-metrics.d.ts +167 -0
  158. package/dist/types/modules/excel/worksheet.d.ts +34 -0
  159. package/dist/types/modules/excel/xlsx/xform/sheet/cf-ext/conditional-formatting-ext-xform.d.ts +1 -1
  160. package/dist/types/modules/excel/xlsx/xform/sheet/cf-ext/conditional-formattings-ext-xform.d.ts +1 -1
  161. package/dist/types/modules/excel/xlsx/xform/sheet/cf-ext/databar-ext-xform.d.ts +1 -1
  162. package/dist/types/modules/excel/xlsx/xform/sheet/cf-ext/icon-set-ext-xform.d.ts +1 -1
  163. package/dist/types/modules/pdf/core/encryption.d.ts +77 -0
  164. package/dist/types/modules/pdf/core/pdf-object.d.ts +65 -0
  165. package/dist/types/modules/pdf/core/pdf-stream.d.ts +195 -0
  166. package/dist/types/modules/pdf/core/pdf-writer.d.ts +84 -0
  167. package/dist/types/modules/pdf/errors.d.ts +33 -0
  168. package/dist/types/modules/pdf/excel-bridge.d.ts +29 -0
  169. package/dist/types/modules/pdf/font/font-embedder.d.ts +49 -0
  170. package/dist/types/modules/pdf/font/font-manager.d.ts +119 -0
  171. package/dist/types/modules/pdf/font/metrics.d.ts +59 -0
  172. package/dist/types/modules/pdf/font/ttf-parser.d.ts +73 -0
  173. package/dist/types/modules/pdf/index.d.ts +37 -0
  174. package/dist/types/modules/pdf/pdf.d.ts +121 -0
  175. package/dist/types/modules/pdf/render/layout-engine.d.ts +25 -0
  176. package/dist/types/modules/pdf/render/page-renderer.d.ts +42 -0
  177. package/dist/types/modules/pdf/render/pdf-exporter.d.ts +19 -0
  178. package/dist/types/modules/pdf/render/png-decoder.d.ts +26 -0
  179. package/dist/types/modules/pdf/render/style-converter.d.ts +67 -0
  180. package/dist/types/modules/pdf/types.d.ts +510 -0
  181. package/package.json +17 -14
package/README.md CHANGED
@@ -6,15 +6,12 @@ Modern TypeScript Excel Workbook Manager - Read, manipulate and write spreadshee
6
6
 
7
7
  ## About This Project
8
8
 
9
- ExcelTS is a modern TypeScript Excel workbook manager with:
9
+ ExcelTS is a zero-dependency TypeScript toolkit for spreadsheets and documents:
10
10
 
11
- - 🚀 **Zero Runtime Dependencies** - Pure TypeScript implementation with no external packages
12
- - **Broad Runtime Support** - LTS Node.js, Bun, and modern browsers (Chrome, Firefox, Safari, Edge)
13
- - ✅ **Full TypeScript Support** - Complete type definitions and modern TypeScript patterns
14
- - ✅ **Modern Build System** - Using Rolldown for faster builds
15
- - ✅ **Enhanced Testing** - Migrated to Vitest with browser testing support
16
- - ✅ **ESM First** - Native ES Module support with CommonJS compatibility
17
- - ✅ **Named Exports** - All exports are named for better tree-shaking
11
+ - 🚀 **Zero Runtime Dependencies** Pure TypeScript, no external packages
12
+ - 📦 **Five Modules** Excel (XLSX/JSON), PDF (standalone engine + Excel bridge), CSV (RFC 4180), Archive (ZIP/TAR), Stream (cross-platform)
13
+ - ✅ **Cross-Platform** Node.js 22+, Bun, Chrome 89+, Firefox 102+, Safari 14.1+
14
+ - ✅ **ESM First** Native ES Modules with CommonJS compatibility and full tree-shaking
18
15
 
19
16
  ## Translations
20
17
 
@@ -103,6 +100,16 @@ cell.fill = {
103
100
  - Hyperlinks
104
101
  - Pivot tables
105
102
 
103
+ - **PDF Export**
104
+ - Full-featured, zero-dependency PDF engine (standalone or with Excel)
105
+ - Full cell styling (fonts, colors, borders, fills, alignment)
106
+ - Automatic pagination with repeat header rows
107
+ - TrueType font embedding for Unicode/CJK text
108
+ - JPEG and PNG image embedding with transparency
109
+ - Password protection and encryption
110
+ - Per-worksheet page setup (size, orientation, margins)
111
+ - Tree-shakeable (not imported = not bundled)
112
+
106
113
  - **Advanced Features**
107
114
  - Streaming for large files
108
115
  - CSV import/export
@@ -131,10 +138,107 @@ import { Readable, pipeline, createTransform } from "@cj-tech-master/excelts/str
131
138
 
132
139
  Each subpath supports `browser`, `import` (ESM), and `require` (CJS) conditions. See the module READMEs for details:
133
140
 
141
+ - [PDF Module](src/modules/pdf/README.md) - Full-featured zero-dependency PDF engine with encryption and font embedding
134
142
  - [CSV Module](src/modules/csv/README.md) - RFC 4180 parser/formatter, streaming, data generation
135
143
  - [Archive Module](src/modules/archive/README.md) - ZIP/TAR create/read/edit, compression, encryption
136
144
  - [Stream Module](src/modules/stream/README.md) - Cross-platform Readable/Writable/Transform/Duplex
137
145
 
146
+ ## PDF Export
147
+
148
+ Export any workbook to PDF with zero external dependencies:
149
+
150
+ ```javascript
151
+ import { Workbook, excelToPdf } from "@cj-tech-master/excelts";
152
+
153
+ const workbook = new Workbook();
154
+ const sheet = workbook.addWorksheet("Report");
155
+ sheet.columns = [
156
+ { header: "Product", key: "product", width: 20 },
157
+ { header: "Revenue", key: "revenue", width: 15 }
158
+ ];
159
+ sheet.addRow({ product: "Widget", revenue: 1000 });
160
+ sheet.getColumn("revenue").numFmt = "$#,##0.00";
161
+
162
+ // One-line export
163
+ const pdf = excelToPdf(workbook, {
164
+ showGridLines: true,
165
+ showPageNumbers: true,
166
+ title: "Sales Report"
167
+ });
168
+
169
+ // Node.js: write to file
170
+ import { writeFileSync } from "fs";
171
+ writeFileSync("report.pdf", pdf);
172
+
173
+ // Browser: download
174
+ const blob = new Blob([pdf], { type: "application/pdf" });
175
+ const url = URL.createObjectURL(blob);
176
+ window.open(url);
177
+ ```
178
+
179
+ ### Convert Existing XLSX to PDF
180
+
181
+ ```javascript
182
+ const workbook = new Workbook();
183
+ await workbook.xlsx.readFile("input.xlsx");
184
+ const pdf = excelToPdf(workbook);
185
+ ```
186
+
187
+ ### Encryption
188
+
189
+ ```javascript
190
+ const pdf = excelToPdf(workbook, {
191
+ encryption: {
192
+ ownerPassword: "admin",
193
+ userPassword: "reader",
194
+ permissions: { print: true, copy: false }
195
+ }
196
+ });
197
+ ```
198
+
199
+ ### Unicode / CJK
200
+
201
+ ```javascript
202
+ import { readFileSync } from "fs";
203
+
204
+ const pdf = excelToPdf(workbook, {
205
+ font: readFileSync("NotoSansSC-Regular.ttf") // TrueType font for CJK text
206
+ });
207
+ ```
208
+
209
+ ### Standalone PDF (No Excel)
210
+
211
+ Generate PDFs from plain data — no workbook, no Map objects, no boilerplate:
212
+
213
+ ```javascript
214
+ import { pdf } from "@cj-tech-master/excelts/pdf";
215
+
216
+ // Simplest — pass a 2D array
217
+ const bytes = pdf([
218
+ ["Product", "Revenue"],
219
+ ["Widget", 1000],
220
+ ["Gadget", 2500]
221
+ ]);
222
+
223
+ // With column widths and styled cells
224
+ const bytes = pdf(
225
+ {
226
+ name: "Report",
227
+ columns: [
228
+ { width: 25, header: "Product" },
229
+ { width: 15, header: "Revenue" }
230
+ ],
231
+ data: [
232
+ ["Widget", 1000],
233
+ ["Gadget", 2500]
234
+ ]
235
+ },
236
+ { showGridLines: true }
237
+ );
238
+ ```
239
+
240
+ For the full API reference and all options, see the [PDF Module documentation](src/modules/pdf/README.md).
241
+
138
242
  ## Archive Utilities (ZIP/TAR)
139
243
 
140
244
  ExcelTS includes internal ZIP/TAR utilities used by the XLSX pipeline. If you use the
@@ -395,6 +499,7 @@ Then open `http://localhost:3000/src/modules/excel/examples/browser-smoke.html`.
395
499
 
396
500
  ### Browser-Specific Notes
397
501
 
502
+ - **PDF export is fully supported** in browsers with zero configuration
398
503
  - **CSV operations are supported** using native RFC 4180 implementation
399
504
  - Use `await workbook.readCsv(input)` to read CSV
400
505
  - Use `workbook.writeCsv()` or `await workbook.writeCsvBuffer()` to write CSV
@@ -428,6 +533,16 @@ import {
428
533
  xmlEncode,
429
534
  xmlDecode,
430
535
 
536
+ // PDF export
537
+ pdf, // Simplest: pdf([["A", 1], ["B", 2]]) → Uint8Array
538
+ excelToPdf, // Workbook -> Uint8Array (Excel-to-PDF)
539
+ PageSizes, // Built-in page size definitions
540
+ PdfError, // Base PDF error
541
+ PdfRenderError, // Layout/rendering failures
542
+ PdfFontError, // Font parsing/embedding failures
543
+ PdfStructureError, // PDF structure assembly failures
544
+ isPdfError, // Type guard for PDF errors
545
+
431
546
  // Error infrastructure
432
547
  BaseError, // Base class for all library errors
433
548
  ExcelError, // Base Excel error (instanceof checks)
@@ -490,6 +605,7 @@ For detailed API documentation, please refer to the comprehensive documentation
490
605
  - Data Validation
491
606
  - Conditional Formatting
492
607
  - File I/O
608
+ - [PDF Export](src/modules/pdf/README.md)
493
609
 
494
610
  ## Contributing Guidelines
495
611
 
package/README_zh.md CHANGED
@@ -6,15 +6,12 @@
6
6
 
7
7
  ## 关于本项目
8
8
 
9
- ExcelTS 是现代化的 TypeScript Excel 工作簿管理器,具有以下特性:
9
+ ExcelTS 是零依赖的 TypeScript 电子表格与文档工具包:
10
10
 
11
- - 🚀 **零运行时依赖** - 纯 TypeScript 实现,无任何外部包依赖
12
- - **广泛运行时支持** - 支持 LTS Node.js、Bun 及主流最新浏览器(Chrome、Firefox、Safari、Edge)
13
- - ✅ **完整的 TypeScript 支持** - 完整的类型定义和现代 TypeScript 模式
14
- - ✅ **现代构建系统** - 使用 Rolldown 进行更快的构建
15
- - ✅ **增强的测试** - 迁移到 Vitest 并支持浏览器测试
16
- - ✅ **ESM 优先** - 原生 ES Module 支持,兼容 CommonJS
17
- - ✅ **命名导出** - 所有导出都是命名导出,更好的 tree-shaking
11
+ - 🚀 **零运行时依赖** 纯 TypeScript 实现,无任何外部包依赖
12
+ - 📦 **五大模块** Excel(XLSX/JSON)、PDF(独立引擎 + Excel 桥接)、CSV(RFC 4180)、Archive(ZIP/TAR)、Stream(跨平台)
13
+ - ✅ **跨平台** Node.js 22+、Bun、Chrome 89+、Firefox 102+、Safari 14.1+
14
+ - ✅ **ESM 优先** 原生 ES Modules,兼容 CommonJS,完整 tree-shaking 支持
18
15
 
19
16
  ## 翻译
20
17
 
@@ -103,6 +100,16 @@ cell.fill = {
103
100
  - 超链接
104
101
  - 数据透视表
105
102
 
103
+ - **PDF 导出**
104
+ - 零依赖 Excel 转 PDF
105
+ - 完整的单元格样式支持(字体、颜色、边框、填充、对齐)
106
+ - 自动分页与重复表头行
107
+ - TrueType 字体嵌入,支持 Unicode/CJK 中文文本
108
+ - JPEG 和 PNG 图片嵌入,支持透明度
109
+ - 密码保护和加密
110
+ - 每个工作表独立的页面设置(纸张大小、方向、页边距)
111
+ - 支持 Tree-shaking(不导入 = 不打包)
112
+
106
113
  - **高级功能**
107
114
  - 大文件流式处理
108
115
  - CSV 导入/导出
@@ -131,10 +138,107 @@ import { Readable, pipeline, createTransform } from "@cj-tech-master/excelts/str
131
138
 
132
139
  每个子路径支持 `browser`、`import`(ESM)和 `require`(CJS)条件。详见各模块文档:
133
140
 
141
+ - [PDF 模块](src/modules/pdf/README.md) - 零依赖 Excel 转 PDF,支持加密和字体嵌入
134
142
  - [CSV 模块](src/modules/csv/README.md) - RFC 4180 解析/格式化、流式处理、数据生成
135
143
  - [归档模块](src/modules/archive/README.md) - ZIP/TAR 创建/读取/编辑、压缩、加密
136
144
  - [流模块](src/modules/stream/README.md) - 跨平台 Readable/Writable/Transform/Duplex
137
145
 
146
+ ## PDF 导出
147
+
148
+ 零依赖将任意工作簿导出为 PDF:
149
+
150
+ ```javascript
151
+ import { Workbook, excelToPdf } from "@cj-tech-master/excelts";
152
+
153
+ const workbook = new Workbook();
154
+ const sheet = workbook.addWorksheet("报告");
155
+ sheet.columns = [
156
+ { header: "产品", key: "product", width: 20 },
157
+ { header: "收入", key: "revenue", width: 15 }
158
+ ];
159
+ sheet.addRow({ product: "组件A", revenue: 1000 });
160
+ sheet.getColumn("revenue").numFmt = "¥#,##0.00";
161
+
162
+ // 一行导出
163
+ const pdf = excelToPdf(workbook, {
164
+ showGridLines: true,
165
+ showPageNumbers: true,
166
+ title: "销售报告"
167
+ });
168
+
169
+ // Node.js:写入文件
170
+ import { writeFileSync } from "fs";
171
+ writeFileSync("report.pdf", pdf);
172
+
173
+ // 浏览器:下载
174
+ const blob = new Blob([pdf], { type: "application/pdf" });
175
+ const url = URL.createObjectURL(blob);
176
+ window.open(url);
177
+ ```
178
+
179
+ ### 将现有 XLSX 转换为 PDF
180
+
181
+ ```javascript
182
+ const workbook = new Workbook();
183
+ await workbook.xlsx.readFile("input.xlsx");
184
+ const pdf = excelToPdf(workbook);
185
+ ```
186
+
187
+ ### 加密
188
+
189
+ ```javascript
190
+ const pdf = excelToPdf(workbook, {
191
+ encryption: {
192
+ ownerPassword: "admin",
193
+ userPassword: "reader",
194
+ permissions: { print: true, copy: false }
195
+ }
196
+ });
197
+ ```
198
+
199
+ ### Unicode / CJK 中文支持
200
+
201
+ ```javascript
202
+ import { readFileSync } from "fs";
203
+
204
+ const pdf = excelToPdf(workbook, {
205
+ font: readFileSync("NotoSansSC-Regular.ttf") // 嵌入 TrueType 字体以支持中文
206
+ });
207
+ ```
208
+
209
+ ### 独立 PDF 生成(无需 Excel)
210
+
211
+ 无需工作簿,直接从数组数据生成 PDF:
212
+
213
+ ```javascript
214
+ import { pdf } from "@cj-tech-master/excelts/pdf";
215
+
216
+ // 最简方式 — 传入二维数组
217
+ const bytes = pdf([
218
+ ["产品", "收入"],
219
+ ["组件A", 1000],
220
+ ["组件B", 2500]
221
+ ]);
222
+
223
+ // 带列宽和样式
224
+ const bytes = pdf(
225
+ {
226
+ name: "报告",
227
+ columns: [
228
+ { width: 25, header: "产品" },
229
+ { width: 15, header: "收入" }
230
+ ],
231
+ data: [
232
+ ["组件A", 1000],
233
+ ["组件B", 2500]
234
+ ]
235
+ },
236
+ { showGridLines: true }
237
+ );
238
+ ```
239
+
240
+ 完整 API 参考和所有选项请查看 [PDF 模块文档](src/modules/pdf/README.md)。
241
+
138
242
  ## 归档工具(ZIP/TAR)
139
243
 
140
244
  ExcelTS 内置 ZIP/TAR 归档工具(用于 XLSX 管线)。若直接使用归档相关 API,
@@ -393,6 +497,7 @@ npx serve .
393
497
 
394
498
  ### 浏览器版本注意事项
395
499
 
500
+ - **完全支持 PDF 导出**(浏览器端零配置即可使用)
396
501
  - **支持 CSV 操作**(使用原生 RFC 4180 标准实现)
397
502
  - 使用 `await workbook.readCsv(input)` 读取 CSV
398
503
  - 使用 `workbook.writeCsv()` 或 `await workbook.writeCsvBuffer()` 写入 CSV
@@ -426,6 +531,16 @@ import {
426
531
  xmlEncode,
427
532
  xmlDecode,
428
533
 
534
+ // PDF 导出
535
+ pdf, // 最简单:pdf([["A", 1], ["B", 2]]) → Uint8Array
536
+ excelToPdf, // Workbook -> Uint8Array(Excel 转 PDF)
537
+ PageSizes, // 内置页面尺寸定义
538
+ PdfError, // PDF 基础错误
539
+ PdfRenderError, // 布局/渲染错误
540
+ PdfFontError, // 字体解析/嵌入错误
541
+ PdfStructureError, // PDF 结构组装错误
542
+ isPdfError, // PDF 错误类型守卫
543
+
429
544
  // 错误基础设施
430
545
  BaseError, // 所有库错误的基类
431
546
  ExcelError, // Excel 基础错误(支持 instanceof 检查)
@@ -488,6 +603,7 @@ import {
488
603
  - 数据验证
489
604
  - 条件格式
490
605
  - 文件输入输出
606
+ - [PDF 导出](src/modules/pdf/README.md)
491
607
 
492
608
  ## 贡献指南
493
609
 
@@ -17,11 +17,10 @@ export * from "./modules/excel/enums.js";
17
17
  export * from "./modules/excel/types.js";
18
18
  export type { PivotTable, PivotTableModel, PivotTableValue, PivotTableSource, CacheField, SharedItemValue, DataField, PivotTableSubtotal, RecordValue, ParsedCacheDefinition, ParsedCacheRecords } from "./modules/excel/pivot-table.js";
19
19
  export type { FormCheckboxModel, FormCheckboxOptions, FormControlRange, FormControlAnchor } from "./modules/excel/form-control.js";
20
- import { WorkbookWriter } from "./modules/excel/stream/workbook-writer.browser.js";
21
- import { WorkbookReader } from "./modules/excel/stream/workbook-reader.browser.js";
22
- import { WorksheetWriter } from "./modules/excel/stream/worksheet-writer.js";
23
- import { WorksheetReader } from "./modules/excel/stream/worksheet-reader.js";
24
- export { WorkbookWriter, WorkbookReader, WorksheetWriter, WorksheetReader };
20
+ export { WorkbookWriter } from "./modules/excel/stream/workbook-writer.browser.js";
21
+ export { WorkbookReader } from "./modules/excel/stream/workbook-reader.browser.js";
22
+ export { WorksheetWriter } from "./modules/excel/stream/worksheet-writer.js";
23
+ export { WorksheetReader } from "./modules/excel/stream/worksheet-reader.js";
25
24
  export type { CsvOptions, CsvInput } from "./modules/excel/workbook.js";
26
25
  export { CsvParserStream, CsvFormatterStream, createCsvParserStream, createCsvFormatterStream } from "./modules/csv/stream/index.js";
27
26
  export { DefinedNames, type DefinedNameModel } from "./modules/excel/defined-names.js";
@@ -38,4 +37,6 @@ export { xmlEncode, xmlDecode } from "./utils/utils.base.js";
38
37
  export { DateParser, DateFormatter, getSupportedFormats, type DateFormat } from "./utils/datetime.js";
39
38
  export { BaseError, type BaseErrorOptions, toError, errorToJSON, getErrorChain, getRootCause } from "./utils/errors.js";
40
39
  export { concatUint8Arrays, toUint8Array, stringToUint8Array, uint8ArrayToString } from "./utils/binary.js";
40
+ export { pdf, excelToPdf, PageSizes, PdfError, PdfRenderError, PdfFontError, PdfStructureError, isPdfError } from "./modules/pdf/index.js";
41
+ export type { PdfExportOptions, PdfPageSize, PdfOrientation, PdfMargins, PageSizeName, PdfColor } from "./modules/pdf/index.js";
41
42
  export { ExcelError, isExcelError, ExcelFileError, ExcelDownloadError, ExcelNotSupportedError, ExcelStreamStateError, InvalidAddressError, ColumnOutOfBoundsError, RowOutOfBoundsError, MergeConflictError, InvalidValueTypeError, XmlParseError, WorksheetNameError, PivotTableError, TableError, ImageError, MaxItemsExceededError } from "./modules/excel/errors.js";
@@ -29,11 +29,10 @@ export * from "./modules/excel/types.js";
29
29
  // Streaming Writer (Browser-compatible)
30
30
  // Uses cross-platform base implementation without Node.js fs
31
31
  // =============================================================================
32
- import { WorkbookWriter } from "./modules/excel/stream/workbook-writer.browser.js";
33
- import { WorkbookReader } from "./modules/excel/stream/workbook-reader.browser.js";
34
- import { WorksheetWriter } from "./modules/excel/stream/worksheet-writer.js";
35
- import { WorksheetReader } from "./modules/excel/stream/worksheet-reader.js";
36
- export { WorkbookWriter, WorkbookReader, WorksheetWriter, WorksheetReader };
32
+ export { WorkbookWriter } from "./modules/excel/stream/workbook-writer.browser.js";
33
+ export { WorkbookReader } from "./modules/excel/stream/workbook-reader.browser.js";
34
+ export { WorksheetWriter } from "./modules/excel/stream/worksheet-writer.js";
35
+ export { WorksheetReader } from "./modules/excel/stream/worksheet-reader.js";
37
36
  export { CsvParserStream, CsvFormatterStream, createCsvParserStream, createCsvFormatterStream } from "./modules/csv/stream/index.js";
38
37
  // =============================================================================
39
38
  // Additional Classes & Types
@@ -57,6 +56,10 @@ export { BaseError, toError, errorToJSON, getErrorChain, getRootCause } from "./
57
56
  // Binary utilities (cross-platform)
58
57
  export { concatUint8Arrays, toUint8Array, stringToUint8Array, uint8ArrayToString } from "./utils/binary.js";
59
58
  // =============================================================================
59
+ // PDF Export (Browser-compatible, zero external dependencies)
60
+ // =============================================================================
61
+ export { pdf, excelToPdf, PageSizes, PdfError, PdfRenderError, PdfFontError, PdfStructureError, isPdfError } from "./modules/pdf/index.js";
62
+ // =============================================================================
60
63
  // Errors
61
64
  // =============================================================================
62
65
  export { ExcelError, isExcelError, ExcelFileError, ExcelDownloadError, ExcelNotSupportedError, ExcelStreamStateError, InvalidAddressError, ColumnOutOfBoundsError, RowOutOfBoundsError, MergeConflictError, InvalidValueTypeError, XmlParseError, WorksheetNameError, PivotTableError, TableError, ImageError, MaxItemsExceededError } from "./modules/excel/errors.js";
@@ -37,4 +37,6 @@ export { xmlEncode, xmlDecode } from "./utils/utils.base.js";
37
37
  export { DateParser, DateFormatter, getSupportedFormats, type DateFormat } from "./utils/datetime.js";
38
38
  export { BaseError, type BaseErrorOptions, toError, errorToJSON, getErrorChain, getRootCause } from "./utils/errors.js";
39
39
  export { concatUint8Arrays, toUint8Array, stringToUint8Array, uint8ArrayToString } from "./utils/binary.js";
40
+ export { pdf, excelToPdf, PageSizes, PdfError, PdfRenderError, PdfFontError, PdfStructureError, isPdfError } from "./modules/pdf/index.js";
41
+ export type { PdfExportOptions, PdfPageSize, PdfOrientation, PdfMargins, PageSizeName, PdfColor } from "./modules/pdf/index.js";
40
42
  export { ExcelError, isExcelError, ExcelFileError, ExcelDownloadError, ExcelNotSupportedError, ExcelStreamStateError, InvalidAddressError, ColumnOutOfBoundsError, RowOutOfBoundsError, MergeConflictError, InvalidValueTypeError, XmlParseError, WorksheetNameError, PivotTableError, TableError, ImageError, MaxItemsExceededError } from "./modules/excel/errors.js";
@@ -52,6 +52,10 @@ export { BaseError, toError, errorToJSON, getErrorChain, getRootCause } from "./
52
52
  // Binary utilities (cross-platform)
53
53
  export { concatUint8Arrays, toUint8Array, stringToUint8Array, uint8ArrayToString } from "./utils/binary.js";
54
54
  // =============================================================================
55
+ // PDF Export
56
+ // =============================================================================
57
+ export { pdf, excelToPdf, PageSizes, PdfError, PdfRenderError, PdfFontError, PdfStructureError, isPdfError } from "./modules/pdf/index.js";
58
+ // =============================================================================
55
59
  // Errors
56
60
  // =============================================================================
57
61
  export { ExcelError, isExcelError, ExcelFileError, ExcelDownloadError, ExcelNotSupportedError, ExcelStreamStateError, InvalidAddressError, ColumnOutOfBoundsError, RowOutOfBoundsError, MergeConflictError, InvalidValueTypeError, XmlParseError, WorksheetNameError, PivotTableError, TableError, ImageError, MaxItemsExceededError } from "./modules/excel/errors.js";
@@ -22,6 +22,13 @@ import { collect } from "../io/archive-sink.js";
22
22
  * Local file header fixed size (30 bytes)
23
23
  */
24
24
  export const LOCAL_HEADER_FIXED_SIZE = 30;
25
+ /**
26
+ * Maximum allowed uncompressed entry size for non-streaming extraction (512 MB).
27
+ * This is a pre-decompression check based on the declared size in the ZIP metadata.
28
+ * It prevents memory exhaustion from archives that declare very large output sizes.
29
+ * The streaming extraction path (processEntryDataStream) has its own byte-level checks.
30
+ */
31
+ const DEFAULT_MAX_ENTRY_SIZE = 512 * 1024 * 1024;
25
32
  /**
26
33
  * Process compressed (and possibly encrypted) entry data to get the final content.
27
34
  *
@@ -36,6 +43,15 @@ export const LOCAL_HEADER_FIXED_SIZE = 30;
36
43
  */
37
44
  export async function processEntryData(entry, compressedData, password, checkCrc32 = false, validateEntrySizes = true) {
38
45
  let result;
46
+ // Pre-decompression size check: reject entries whose *declared* uncompressed size
47
+ // exceeds the limit. This catches archives that honestly declare very large entries
48
+ // but does NOT protect against ZIP bombs that lie about their size (for that, use
49
+ // the streaming path processEntryDataStream which validates actual output bytes).
50
+ if (validateEntrySizes && entry.uncompressedSize > DEFAULT_MAX_ENTRY_SIZE) {
51
+ throw new Error(`Entry "${entry.path}" declares uncompressed size of ${entry.uncompressedSize} bytes, ` +
52
+ `which exceeds the maximum allowed size of ${DEFAULT_MAX_ENTRY_SIZE} bytes. ` +
53
+ "Use the streaming API for large entries.");
54
+ }
39
55
  // Handle encrypted entries
40
56
  if (entry.isEncrypted) {
41
57
  if (!password) {
@@ -99,6 +115,12 @@ export async function processEntryData(entry, compressedData, password, checkCrc
99
115
  */
100
116
  export function processEntryDataSync(entry, compressedData, password, validateEntrySizes = true) {
101
117
  let result;
118
+ // Pre-decompression size check (same as async version)
119
+ if (validateEntrySizes && entry.uncompressedSize > DEFAULT_MAX_ENTRY_SIZE) {
120
+ throw new Error(`Entry "${entry.path}" declares uncompressed size of ${entry.uncompressedSize} bytes, ` +
121
+ `which exceeds the maximum allowed size of ${DEFAULT_MAX_ENTRY_SIZE} bytes. ` +
122
+ "Use the streaming API for large entries.");
123
+ }
102
124
  // Handle encrypted entries
103
125
  if (entry.isEncrypted) {
104
126
  if (!password) {
@@ -31,17 +31,17 @@ export type ZipDeflateFileDefaults = {
31
31
  export declare function buildZipDeflateFileOptions(entryOptions: ZipDeflateFileEntryOptions | undefined, defaults: ZipDeflateFileDefaults): {
32
32
  level: number;
33
33
  modTime: Date;
34
- atime: Date;
35
- ctime: Date;
36
- birthTime: Date;
34
+ atime: Date | undefined;
35
+ ctime: Date | undefined;
36
+ birthTime: Date | undefined;
37
37
  timestamps: ZipTimestampMode;
38
- comment: string;
38
+ comment: string | undefined;
39
39
  smartStore: boolean;
40
40
  zip64: Zip64Mode;
41
41
  path: false | ZipPathOptions;
42
- encoding: ZipStringEncoding;
43
- mode: number;
44
- msDosAttributes: number;
45
- externalAttributes: number;
46
- versionMadeBy: number;
42
+ encoding: ZipStringEncoding | undefined;
43
+ mode: number | undefined;
44
+ msDosAttributes: number | undefined;
45
+ externalAttributes: number | undefined;
46
+ versionMadeBy: number | undefined;
47
47
  };
@@ -13,6 +13,18 @@ import { CENTRAL_DIR_HEADER_SIG, COMPRESSION_AES, UINT16_MAX, UINT32_MAX, ZIP64_
13
13
  // -----------------------------------------------------------------------------
14
14
  // Constants
15
15
  // -----------------------------------------------------------------------------
16
+ const MAX_SAFE = BigInt(Number.MAX_SAFE_INTEGER);
17
+ /**
18
+ * Convert a BigInt to Number, throwing if the value exceeds Number.MAX_SAFE_INTEGER.
19
+ * This prevents silent precision loss when parsing ZIP64 fields.
20
+ */
21
+ function safeBigIntToNumber(value, fieldName) {
22
+ if (value > MAX_SAFE) {
23
+ throw new Error(`ZIP64 ${fieldName} value ${value} exceeds Number.MAX_SAFE_INTEGER. ` +
24
+ "The archive may be corrupted or malicious.");
25
+ }
26
+ return Number(value);
27
+ }
16
28
  /** Minimum EOCD size (22 bytes fixed + 0-byte comment) */
17
29
  export const EOCD_MIN_SIZE = 22;
18
30
  /** Maximum comment size (2^16 - 1) */
@@ -129,7 +141,7 @@ export function parseZIP64EOCDLocator(data, offset) {
129
141
  return -1;
130
142
  }
131
143
  reader.skip(4); // disk number with ZIP64 EOCD
132
- return Number(reader.readBigUint64());
144
+ return safeBigIntToNumber(reader.readBigUint64(), "EOCD locator offset");
133
145
  }
134
146
  /**
135
147
  * Parse ZIP64 EOCD.
@@ -167,16 +179,16 @@ export function parseZIP64EOCD(data, offset) {
167
179
  */
168
180
  export function applyZIP64ToEOCD(eocd, zip64) {
169
181
  if (eocd.totalEntries === UINT16_MAX) {
170
- eocd.totalEntries = Number(zip64.totalEntries);
182
+ eocd.totalEntries = safeBigIntToNumber(zip64.totalEntries, "totalEntries");
171
183
  }
172
184
  if (eocd.entriesOnDisk === UINT16_MAX) {
173
- eocd.entriesOnDisk = Number(zip64.entriesOnDisk);
185
+ eocd.entriesOnDisk = safeBigIntToNumber(zip64.entriesOnDisk, "entriesOnDisk");
174
186
  }
175
187
  if (eocd.centralDirSize === UINT32_MAX) {
176
- eocd.centralDirSize = Number(zip64.centralDirSize);
188
+ eocd.centralDirSize = safeBigIntToNumber(zip64.centralDirSize, "centralDirSize");
177
189
  }
178
190
  if (eocd.centralDirOffset === UINT32_MAX) {
179
- eocd.centralDirOffset = Number(zip64.centralDirOffset);
191
+ eocd.centralDirOffset = safeBigIntToNumber(zip64.centralDirOffset, "centralDirOffset");
180
192
  }
181
193
  }
182
194
  // -----------------------------------------------------------------------------
@@ -134,7 +134,12 @@ export function formatField(value, regex, ctx) {
134
134
  // Escape formulae to prevent CSV injection (OWASP recommendation)
135
135
  // Prefix dangerous characters with single quote to neutralize them in spreadsheet apps
136
136
  // Using single quote (') as recommended by OWASP, which Excel interprets as a text prefix
137
- if (escapeFormulae && transformQuoteHint !== false && startsWithFormulaChar(str)) {
137
+ // Skip numeric types: negative numbers like -5.55 are not formula injection vectors
138
+ if (escapeFormulae &&
139
+ transformQuoteHint !== false &&
140
+ typeof value !== "number" &&
141
+ typeof value !== "bigint" &&
142
+ startsWithFormulaChar(str)) {
138
143
  str = "'" + str;
139
144
  }
140
145
  // If quoting is disabled, return raw string
@@ -11,8 +11,8 @@
11
11
  */
12
12
  export type { HeaderArray, RowHashArray, RowArray, RowMap, Row, HeaderTransformFunction, RowTransformCallback, RowTransformFunction, RowValidateCallback, RowValidateFunction, TransformContext, FormattedValue, TransformResult, TypeTransformMap, DynamicTypingConfig, CastDateConfig, ColumnConfig, CsvErrorCode, CsvRecordError, OnSkipCallback, CsvParseOptions, CsvFormatOptions, CsvParseArrayOptions, CsvParseObjectOptions, ChunkMeta, CsvParseMeta, RecordInfo, RecordWithInfo, CsvParseResult, CsvParseResultWithObjname, ColumnMismatchConfig, ColumnMismatchLess, ColumnMismatchMore } from "./types.js";
13
13
  export { isFormattedValue, quoted, unquoted } from "./types.js";
14
- export { parseCsv } from "./parse/index.js";
15
- export { parseCsvAsync, parseCsvRows, parseCsvWithProgress } from "./parse/index.js";
14
+ export { parseCsv } from "./parse/sync.js";
15
+ export { parseCsvAsync, parseCsvRows, parseCsvWithProgress } from "./parse/async.js";
16
16
  export { formatCsv } from "./format/index.js";
17
17
  export { CsvParserStream, CsvFormatterStream, createCsvParserStream, createCsvFormatterStream } from "./stream/index.js";
18
18
  export { detectDelimiter, detectLinebreak, stripBom } from "./utils/detect.js";
@@ -14,10 +14,10 @@ export { isFormattedValue, quoted, unquoted } from "./types.js";
14
14
  // =============================================================================
15
15
  // Core Functions
16
16
  // =============================================================================
17
- // Synchronous parser
18
- export { parseCsv } from "./parse/index.js";
17
+ // Synchronous parser (direct import to avoid barrel pulling in async -> stream)
18
+ export { parseCsv } from "./parse/sync.js";
19
19
  // Async parser
20
- export { parseCsvAsync, parseCsvRows, parseCsvWithProgress } from "./parse/index.js";
20
+ export { parseCsvAsync, parseCsvRows, parseCsvWithProgress } from "./parse/async.js";
21
21
  // Formatter
22
22
  export { formatCsv } from "./format/index.js";
23
23
  // =============================================================================
@@ -401,7 +401,7 @@ export function parseCsv(input, options = {}) {
401
401
  // Handle objname option
402
402
  const { objname } = options;
403
403
  if (objname && state.headerRow) {
404
- const objResult = {};
404
+ const objResult = Object.create(null);
405
405
  for (const item of objectRows) {
406
406
  const rec = config.infoOption
407
407
  ? item.record
@@ -409,6 +409,11 @@ export function parseCsv(input, options = {}) {
409
409
  const key = rec[objname];
410
410
  // Convert undefined/null to empty string, otherwise convert to string
411
411
  const keyStr = key === undefined || key === null ? "" : String(key);
412
+ // Skip __proto__ to prevent prototype pollution via JSON.
413
+ // Note: constructor/prototype are safe on Object.create(null) objects.
414
+ if (keyStr === "__proto__") {
415
+ continue;
416
+ }
412
417
  objResult[keyStr] = item;
413
418
  }
414
419
  return {