@shbernal/ts-xlsx 3.1.0 → 3.2.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.
Files changed (214) hide show
  1. package/README.md +30 -23
  2. package/dist/bytes.d.ts +3 -2
  3. package/dist/core/address.d.ts +36 -0
  4. package/dist/core/address.js +47 -9
  5. package/dist/core/array-formula-ranges.d.ts +33 -0
  6. package/dist/core/array-formula-ranges.js +62 -0
  7. package/dist/core/color-resolution.js +2 -2
  8. package/dist/core/column.js +3 -2
  9. package/dist/core/comment-thread.d.ts +7 -3
  10. package/dist/core/conditional-formatting-overlay.d.ts +9 -0
  11. package/dist/core/conditional-formatting-overlay.js +28 -1
  12. package/dist/core/conditional-formatting.d.ts +98 -10
  13. package/dist/core/conditional-formatting.js +42 -1
  14. package/dist/core/data-validation-overlay.d.ts +5 -0
  15. package/dist/core/data-validation-overlay.js +15 -0
  16. package/dist/core/data-validation.d.ts +16 -0
  17. package/dist/core/data-validation.js +15 -0
  18. package/dist/core/date-format.js +14 -4
  19. package/dist/core/date.js +1 -1
  20. package/dist/core/formula-references.d.ts +26 -0
  21. package/dist/core/formula-references.js +173 -0
  22. package/dist/core/formula-scan.d.ts +2 -0
  23. package/dist/core/formula-scan.js +58 -0
  24. package/dist/core/formula.d.ts +51 -18
  25. package/dist/core/formula.js +77 -86
  26. package/dist/core/function-values.d.ts +4 -0
  27. package/dist/core/function-values.js +58 -0
  28. package/dist/core/future-functions.d.ts +3 -0
  29. package/dist/core/{modern-functions.js → future-functions.js} +119 -88
  30. package/dist/core/grid-edits.d.ts +28 -0
  31. package/dist/core/grid-edits.js +193 -25
  32. package/dist/core/hyperlink.d.ts +39 -0
  33. package/dist/core/hyperlink.js +88 -0
  34. package/dist/core/image.d.ts +36 -5
  35. package/dist/core/image.js +9 -0
  36. package/dist/core/internal.d.ts +3 -1
  37. package/dist/core/limits.d.ts +5 -3
  38. package/dist/core/merge.d.ts +13 -0
  39. package/dist/core/merge.js +15 -1
  40. package/dist/core/page-setup.d.ts +26 -11
  41. package/dist/core/pivot-table.d.ts +49 -10
  42. package/dist/core/pivot-table.js +55 -21
  43. package/dist/core/protection.d.ts +66 -4
  44. package/dist/core/protection.js +4 -0
  45. package/dist/core/row.js +3 -2
  46. package/dist/core/structured-reference.d.ts +7 -0
  47. package/dist/core/structured-reference.js +7 -0
  48. package/dist/core/table.d.ts +38 -8
  49. package/dist/core/table.js +37 -7
  50. package/dist/core/theme.d.ts +2 -0
  51. package/dist/core/theme.js +4 -1
  52. package/dist/core/value.d.ts +51 -28
  53. package/dist/core/value.js +15 -12
  54. package/dist/core/workbook-styles.d.ts +17 -1
  55. package/dist/core/workbook-styles.js +4 -0
  56. package/dist/core/workbook-theme.js +5 -3
  57. package/dist/core/workbook.d.ts +20 -3
  58. package/dist/core/workbook.js +26 -5
  59. package/dist/core/worksheet-merges.d.ts +9 -4
  60. package/dist/core/worksheet-merges.js +17 -11
  61. package/dist/core/worksheet-model.d.ts +1 -1
  62. package/dist/core/worksheet-model.js +4 -0
  63. package/dist/core/worksheet-pictures.d.ts +3 -3
  64. package/dist/core/worksheet-pictures.js +7 -6
  65. package/dist/core/worksheet.d.ts +110 -21
  66. package/dist/core/worksheet.js +174 -49
  67. package/dist/entries/core.d.ts +7 -5
  68. package/dist/entries/core.js +4 -3
  69. package/dist/entries/errors.d.ts +1 -0
  70. package/dist/entries/errors.js +1 -0
  71. package/dist/errors.js +1 -1
  72. package/dist/io/cell-metadata/metadata.d.ts +42 -0
  73. package/dist/io/cell-metadata/metadata.js +35 -0
  74. package/dist/io/cell-metadata/rich-values.d.ts +11 -0
  75. package/dist/io/cell-metadata/rich-values.js +80 -0
  76. package/dist/io/csv/delimiter.d.ts +14 -2
  77. package/dist/io/csv/delimiter.js +12 -0
  78. package/dist/io/csv/errors.d.ts +14 -0
  79. package/dist/io/csv/errors.js +5 -0
  80. package/dist/io/csv/read.d.ts +6 -1
  81. package/dist/io/csv/read.js +46 -24
  82. package/dist/io/csv/write.d.ts +10 -3
  83. package/dist/io/csv/write.js +16 -7
  84. package/dist/io/opc/part-paths.js +4 -4
  85. package/dist/io/opc/read-opc.d.ts +4 -0
  86. package/dist/io/opc/read-opc.js +10 -4
  87. package/dist/io/opc/strict-relationships.d.ts +2 -0
  88. package/dist/io/opc/strict-relationships.js +12 -0
  89. package/dist/io/read-policy/column-budget.d.ts +26 -0
  90. package/dist/io/{xlsx → read-policy}/column-budget.js +4 -8
  91. package/dist/io/read-policy/read-repair.d.ts +74 -0
  92. package/dist/io/read-policy/read-repair.js +119 -0
  93. package/dist/io/style/cell-style-resolution.d.ts +25 -0
  94. package/dist/io/style/cell-style-resolution.js +30 -0
  95. package/dist/io/xlsb/formula.js +4 -2
  96. package/dist/io/xlsb/ptg-functions.js +110 -0
  97. package/dist/io/xlsb/read-metadata.d.ts +7 -0
  98. package/dist/io/xlsb/read-metadata.js +69 -0
  99. package/dist/io/xlsb/read-worksheet.d.ts +6 -0
  100. package/dist/io/xlsb/read-worksheet.js +95 -37
  101. package/dist/io/xlsb/read.js +26 -8
  102. package/dist/io/xlsb/record-types.d.ts +13 -0
  103. package/dist/io/xlsb/record-types.js +13 -0
  104. package/dist/io/xlsb/sheet-protection.d.ts +20 -0
  105. package/dist/io/xlsb/sheet-protection.js +60 -0
  106. package/dist/io/xlsx/cell-accumulator.d.ts +20 -12
  107. package/dist/io/xlsx/cell-accumulator.js +49 -19
  108. package/dist/io/xlsx/cell-metadata.d.ts +39 -0
  109. package/dist/io/xlsx/cell-metadata.js +162 -0
  110. package/dist/io/xlsx/cell-value.d.ts +16 -4
  111. package/dist/io/xlsx/cell-value.js +38 -21
  112. package/dist/io/xlsx/color-xml.js +1 -1
  113. package/dist/io/xlsx/column-span.d.ts +11 -0
  114. package/dist/io/xlsx/column-span.js +9 -0
  115. package/dist/io/xlsx/comments.d.ts +3 -28
  116. package/dist/io/xlsx/comments.js +3 -65
  117. package/dist/io/xlsx/conditional-formatting.d.ts +42 -25
  118. package/dist/io/xlsx/conditional-formatting.js +424 -129
  119. package/dist/io/xlsx/data-validation.d.ts +12 -7
  120. package/dist/io/xlsx/data-validation.js +41 -29
  121. package/dist/io/xlsx/edit-vba.js +43 -36
  122. package/dist/io/xlsx/hyperlinks.d.ts +16 -50
  123. package/dist/io/xlsx/hyperlinks.js +8 -37
  124. package/dist/io/xlsx/images.d.ts +49 -16
  125. package/dist/io/xlsx/images.js +145 -44
  126. package/dist/io/xlsx/namespaces.d.ts +8 -0
  127. package/dist/io/xlsx/namespaces.js +4 -0
  128. package/dist/io/xlsx/package-plan.d.ts +48 -15
  129. package/dist/io/xlsx/package-plan.js +46 -9
  130. package/dist/io/xlsx/part-names.d.ts +6 -8
  131. package/dist/io/xlsx/part-names.js +3 -7
  132. package/dist/io/xlsx/pivot.d.ts +5 -5
  133. package/dist/io/xlsx/pivot.js +23 -22
  134. package/dist/io/xlsx/preserved-splices.d.ts +6 -0
  135. package/dist/io/xlsx/preserved-splices.js +74 -0
  136. package/dist/io/xlsx/read-comments.d.ts +29 -0
  137. package/dist/io/xlsx/read-comments.js +75 -0
  138. package/dist/io/xlsx/read-parts.d.ts +2 -2
  139. package/dist/io/xlsx/read-parts.js +44 -37
  140. package/dist/io/xlsx/read-pivot.js +9 -3
  141. package/dist/io/xlsx/read-rich-runs.d.ts +8 -5
  142. package/dist/io/xlsx/read-rich-runs.js +18 -1
  143. package/dist/io/xlsx/read-rows.d.ts +18 -7
  144. package/dist/io/xlsx/read-rows.js +47 -37
  145. package/dist/io/xlsx/read-styles.d.ts +0 -25
  146. package/dist/io/xlsx/read-styles.js +39 -34
  147. package/dist/io/xlsx/read-workbook-xml.d.ts +6 -0
  148. package/dist/io/xlsx/read-workbook-xml.js +9 -4
  149. package/dist/io/xlsx/read-worksheet.d.ts +8 -3
  150. package/dist/io/xlsx/read-worksheet.js +45 -23
  151. package/dist/io/xlsx/read.d.ts +0 -4
  152. package/dist/io/xlsx/read.js +17 -21
  153. package/dist/io/xlsx/relationships.d.ts +3 -0
  154. package/dist/io/xlsx/relationships.js +4 -0
  155. package/dist/io/xlsx/row-position.d.ts +3 -1
  156. package/dist/io/xlsx/row-xml.d.ts +18 -7
  157. package/dist/io/xlsx/row-xml.js +54 -35
  158. package/dist/io/xlsx/sheet-properties.js +68 -42
  159. package/dist/io/xlsx/strict-parts.d.ts +7 -0
  160. package/dist/io/xlsx/strict-parts.js +171 -0
  161. package/dist/io/xlsx/style-elements.d.ts +0 -2
  162. package/dist/io/xlsx/style-elements.js +9 -29
  163. package/dist/io/xlsx/styles.d.ts +5 -0
  164. package/dist/io/xlsx/styles.js +11 -5
  165. package/dist/io/xlsx/tables.d.ts +10 -2
  166. package/dist/io/xlsx/tables.js +49 -42
  167. package/dist/io/xlsx/theme-xml.d.ts +6 -0
  168. package/dist/io/xlsx/theme-xml.js +16 -19
  169. package/dist/io/xlsx/threaded-comments.d.ts +6 -1
  170. package/dist/io/xlsx/threaded-comments.js +14 -7
  171. package/dist/io/xlsx/workbook-xml.d.ts +13 -24
  172. package/dist/io/xlsx/workbook-xml.js +15 -33
  173. package/dist/io/xlsx/worksheet-xml.d.ts +5 -2
  174. package/dist/io/xlsx/worksheet-xml.js +37 -69
  175. package/dist/io/xlsx/write-stream.d.ts +28 -6
  176. package/dist/io/xlsx/write-stream.js +42 -17
  177. package/dist/io/xlsx/write.d.ts +11 -4
  178. package/dist/io/xlsx/write.js +153 -90
  179. package/dist/rel-type.d.ts +9 -0
  180. package/dist/rel-type.js +3 -0
  181. package/dist/vba/bytes.d.ts +2 -0
  182. package/dist/vba/bytes.js +11 -0
  183. package/dist/vba/cfb-format.d.ts +47 -0
  184. package/dist/vba/cfb-format.js +34 -0
  185. package/dist/vba/cfb-writer.d.ts +1 -2
  186. package/dist/vba/cfb-writer.js +33 -36
  187. package/dist/vba/cfb.d.ts +9 -4
  188. package/dist/vba/cfb.js +55 -43
  189. package/dist/vba/dir-records.d.ts +6 -0
  190. package/dist/vba/dir-records.js +10 -2
  191. package/dist/vba/errors.d.ts +4 -4
  192. package/dist/vba/index.d.ts +0 -2
  193. package/dist/vba/index.js +0 -2
  194. package/dist/vba/ms-ovba.d.ts +6 -2
  195. package/dist/vba/ms-ovba.js +15 -6
  196. package/dist/vba/project-editor.js +102 -87
  197. package/dist/vba/project.d.ts +4 -0
  198. package/dist/vba/project.js +12 -6
  199. package/dist/vba/vba-encoding.d.ts +4 -10
  200. package/dist/vba/vba-encoding.js +2 -15
  201. package/dist/xml/xml-read.d.ts +29 -1
  202. package/dist/xml/xml-read.js +56 -0
  203. package/dist/xml/xml-scan.d.ts +13 -0
  204. package/dist/xml/xml-scan.js +58 -7
  205. package/dist/xml/xml.d.ts +33 -1
  206. package/dist/xml/xml.js +22 -0
  207. package/package.json +5 -4
  208. package/skills/ts-xlsx-upstream/SKILL.md +21 -13
  209. package/dist/core/modern-functions.d.ts +0 -1
  210. package/dist/io/xlsx/cell-style-resolution.d.ts +0 -25
  211. package/dist/io/xlsx/cell-style-resolution.js +0 -29
  212. package/dist/io/xlsx/column-budget.d.ts +0 -26
  213. package/dist/io/xlsx/read-repair.d.ts +0 -26
  214. package/dist/io/xlsx/read-repair.js +0 -39
@@ -0,0 +1,30 @@
1
+ export class CellStyleResolver {
2
+ #columnStyle = new Map();
3
+ #rowStyle = -1;
4
+ noteColumnSpan(first, last, styleIndex) {
5
+ if (styleIndex < 0)
6
+ return;
7
+ for (let index = first; index <= last; index++)
8
+ this.#columnStyle.set(index, styleIndex);
9
+ }
10
+ openRow(styleIndex, customFormat) {
11
+ this.#rowStyle = customFormat ? styleIndex : -1;
12
+ }
13
+ closeRow() {
14
+ this.#rowStyle = -1;
15
+ }
16
+ styleFor(col, cellStyleIndex, xfStyles) {
17
+ const index = this.#indexFor(col, cellStyleIndex);
18
+ return xfStyles[index < 0 ? 0 : index];
19
+ }
20
+ declaresFormat(col, cellStyleIndex) {
21
+ return this.#indexFor(col, cellStyleIndex) >= 0;
22
+ }
23
+ #indexFor(col, cellStyleIndex) {
24
+ if (cellStyleIndex >= 0)
25
+ return cellStyleIndex;
26
+ if (this.#rowStyle >= 0)
27
+ return this.#rowStyle;
28
+ return this.#columnStyle.get(col) ?? -1;
29
+ }
30
+ }
@@ -22,7 +22,7 @@ function decodeTokens(rgce, rgcb, scope) {
22
22
  stack,
23
23
  scope,
24
24
  push: (text) => {
25
- if (text === undefined)
25
+ if (text === undefined || text.length > MAX_DECODED_FORMULA_LENGTH)
26
26
  return false;
27
27
  stack.push(text);
28
28
  return true;
@@ -81,7 +81,7 @@ function step(ptg, decode) {
81
81
  case PTG.Num:
82
82
  return push(numberLiteral(tokens.f64()));
83
83
  default:
84
- return ptg >= CLASSED_TOKEN_FLOOR
84
+ return ptg >= CLASSED_TOKEN_FLOOR && ptg <= CLASSED_TOKEN_CEILING
85
85
  ? operand((ptg & CLASSED_TOKEN_MASK) | CLASSED_TOKEN_FLOOR, decode)
86
86
  : false;
87
87
  }
@@ -299,7 +299,9 @@ const PTG = {
299
299
  AreaErr3d: 0x3d,
300
300
  };
301
301
  const CLASSED_TOKEN_FLOOR = 0x20;
302
+ const CLASSED_TOKEN_CEILING = 0x7f;
302
303
  const CLASSED_TOKEN_MASK = 0x1f;
304
+ const MAX_DECODED_FORMULA_LENGTH = 4 * 8_192;
303
305
  const ATTR_CHOOSE = 0x04;
304
306
  const ATTR_SUM = 0x10;
305
307
  const FUNCVAR_PARAM_MASK = 0x7f;
@@ -398,6 +398,116 @@ const RUNS = [
398
398
  ['RTD', 'variadic'],
399
399
  ],
400
400
  ],
401
+ [
402
+ 0x017c,
403
+ [
404
+ ['CUBEVALUE', 'variadic'],
405
+ ['CUBEMEMBER', 'variadic'],
406
+ ['CUBEMEMBERPROPERTY', 3],
407
+ ['CUBERANKEDMEMBER', 'variadic'],
408
+ ['HEX2BIN', 'variadic'],
409
+ ['HEX2DEC', 1],
410
+ ['HEX2OCT', 'variadic'],
411
+ ['DEC2BIN', 'variadic'],
412
+ ['DEC2HEX', 'variadic'],
413
+ ['DEC2OCT', 'variadic'],
414
+ ['OCT2BIN', 'variadic'],
415
+ ['OCT2HEX', 'variadic'],
416
+ ['OCT2DEC', 1],
417
+ ['BIN2DEC', 1],
418
+ ['BIN2OCT', 'variadic'],
419
+ ['BIN2HEX', 'variadic'],
420
+ ['IMSUB', 2],
421
+ ['IMDIV', 2],
422
+ ['IMPOWER', 2],
423
+ ['IMABS', 1],
424
+ ['IMSQRT', 1],
425
+ ['IMLN', 1],
426
+ ['IMLOG2', 1],
427
+ ['IMLOG10', 1],
428
+ ['IMSIN', 1],
429
+ ['IMCOS', 1],
430
+ ['IMEXP', 1],
431
+ ['IMARGUMENT', 1],
432
+ ['IMCONJUGATE', 1],
433
+ ['IMAGINARY', 1],
434
+ ['IMREAL', 1],
435
+ ['COMPLEX', 'variadic'],
436
+ ['IMSUM', 'variadic'],
437
+ ['IMPRODUCT', 'variadic'],
438
+ ['SERIESSUM', 4],
439
+ ['FACTDOUBLE', 1],
440
+ ['SQRTPI', 1],
441
+ ['QUOTIENT', 2],
442
+ ['DELTA', 'variadic'],
443
+ ['GESTEP', 'variadic'],
444
+ ['ISEVEN', 1],
445
+ ['ISODD', 1],
446
+ ['MROUND', 2],
447
+ ['ERF', 'variadic'],
448
+ ['ERFC', 1],
449
+ ['BESSELJ', 2],
450
+ ['BESSELK', 2],
451
+ ['BESSELY', 2],
452
+ ['BESSELI', 2],
453
+ ['XIRR', 'variadic'],
454
+ ['XNPV', 3],
455
+ ['PRICEMAT', 'variadic'],
456
+ ['YIELDMAT', 'variadic'],
457
+ ['INTRATE', 'variadic'],
458
+ ['RECEIVED', 'variadic'],
459
+ ['DISC', 'variadic'],
460
+ ['PRICEDISC', 'variadic'],
461
+ ['YIELDDISC', 'variadic'],
462
+ ['TBILLEQ', 3],
463
+ ['TBILLPRICE', 3],
464
+ ['TBILLYIELD', 3],
465
+ ['PRICE', 'variadic'],
466
+ ['YIELD', 'variadic'],
467
+ ['DOLLARDE', 2],
468
+ ['DOLLARFR', 2],
469
+ ['NOMINAL', 2],
470
+ ['EFFECT', 2],
471
+ ['CUMPRINC', 6],
472
+ ['CUMIPMT', 6],
473
+ ['EDATE', 2],
474
+ ['EOMONTH', 2],
475
+ ['YEARFRAC', 'variadic'],
476
+ ['COUPDAYBS', 'variadic'],
477
+ ['COUPDAYS', 'variadic'],
478
+ ['COUPDAYSNC', 'variadic'],
479
+ ['COUPNCD', 'variadic'],
480
+ ['COUPNUM', 'variadic'],
481
+ ['COUPPCD', 'variadic'],
482
+ ['DURATION', 'variadic'],
483
+ ['MDURATION', 'variadic'],
484
+ ['ODDLPRICE', 'variadic'],
485
+ ['ODDLYIELD', 'variadic'],
486
+ ['ODDFPRICE', 'variadic'],
487
+ ['ODDFYIELD', 'variadic'],
488
+ ['RANDBETWEEN', 2],
489
+ ['WEEKNUM', 'variadic'],
490
+ ['AMORDEGRC', 'variadic'],
491
+ ['AMORLINC', 'variadic'],
492
+ ['CONVERT', 3],
493
+ ['ACCRINT', 'variadic'],
494
+ ['ACCRINTM', 'variadic'],
495
+ ['WORKDAY', 'variadic'],
496
+ ['NETWORKDAYS', 'variadic'],
497
+ ['GCD', 'variadic'],
498
+ ['MULTINOMIAL', 'variadic'],
499
+ ['LCM', 'variadic'],
500
+ ['FVSCHEDULE', 2],
501
+ ['CUBEKPIMEMBER', 'variadic'],
502
+ ['CUBESET', 'variadic'],
503
+ ['CUBESETCOUNT', 1],
504
+ ['IFERROR', 2],
505
+ ['COUNTIFS', 'variadic'],
506
+ ['SUMIFS', 'variadic'],
507
+ ['AVERAGEIF', 'variadic'],
508
+ ['AVERAGEIFS', 'variadic'],
509
+ ],
510
+ ],
401
511
  ];
402
512
  const TABLE = [];
403
513
  for (const [start, entries] of RUNS) {
@@ -0,0 +1,7 @@
1
+ import { type WorkbookMetadata } from '../cell-metadata/metadata.ts';
2
+ /**
3
+ * Read `xl/metadata.bin` as far as a cell's metadata indices reach into it.
4
+ *
5
+ * @throws {XlsbParseError} if a record the model reads is shorter than its fields.
6
+ */
7
+ export declare function parseMetadataPart(part: Uint8Array | undefined): WorkbookMetadata;
@@ -0,0 +1,69 @@
1
+ import { DYNAMIC_ARRAY_TYPE, NO_METADATA, RICH_VALUE_TYPE, } from '../cell-metadata/metadata.js';
2
+ import { RecordReader } from './primitives.js';
3
+ import { readRecords } from './record-stream.js';
4
+ import { BRT } from './record-types.js';
5
+ export function parseMetadataPart(part) {
6
+ if (part === undefined)
7
+ return NO_METADATA;
8
+ const typeNames = [];
9
+ const dynamicArrayBlocks = [];
10
+ const richValueBlocks = [];
11
+ const cellBlocks = [];
12
+ const valueBlocks = [];
13
+ let futureType;
14
+ let blocks;
15
+ for (const record of readRecords(part)) {
16
+ const reader = new RecordReader(record.data);
17
+ switch (record.type) {
18
+ case BRT.Mdtinfo:
19
+ reader.skip(8);
20
+ typeNames.push(reader.wideString());
21
+ break;
22
+ case BRT.BeginEsfmd:
23
+ reader.skip(4);
24
+ futureType = reader.wideString();
25
+ break;
26
+ case BRT.EndEsfmd:
27
+ futureType = undefined;
28
+ break;
29
+ case BRT.BeginFmd:
30
+ if (futureType === DYNAMIC_ARRAY_TYPE)
31
+ dynamicArrayBlocks.push(false);
32
+ else if (futureType === RICH_VALUE_TYPE)
33
+ richValueBlocks.push(-1);
34
+ break;
35
+ case BRT.DynamicArrayProperties:
36
+ if (futureType === DYNAMIC_ARRAY_TYPE && dynamicArrayBlocks.length > 0) {
37
+ dynamicArrayBlocks[dynamicArrayBlocks.length - 1] = (reader.u8() & 1) !== 0;
38
+ }
39
+ break;
40
+ case BRT.RichValueBlock:
41
+ if (futureType === RICH_VALUE_TYPE && richValueBlocks.length > 0) {
42
+ reader.skip(4);
43
+ richValueBlocks[richValueBlocks.length - 1] = reader.u32();
44
+ }
45
+ break;
46
+ case BRT.BeginEsmdb:
47
+ reader.skip(4);
48
+ blocks = (reader.u32() & 1) !== 0 ? cellBlocks : valueBlocks;
49
+ break;
50
+ case BRT.EndEsmdb:
51
+ blocks = undefined;
52
+ break;
53
+ case BRT.Mdb:
54
+ blocks?.push(metadataRecords(reader));
55
+ break;
56
+ default:
57
+ break;
58
+ }
59
+ }
60
+ return { typeNames, dynamicArrayBlocks, richValueBlocks, cellBlocks, valueBlocks };
61
+ }
62
+ function metadataRecords(reader) {
63
+ const count = reader.u32();
64
+ const records = [];
65
+ while (records.length < count && reader.remaining >= 8) {
66
+ records.push({ type: reader.u32(), value: reader.u32() });
67
+ }
68
+ return records;
69
+ }
@@ -1,5 +1,6 @@
1
1
  import { type DateEpoch } from '../../core/date.ts';
2
2
  import type { Worksheet } from '../../core/worksheet.ts';
3
+ import type { CellMetadataIndex } from '../cell-metadata/metadata.ts';
3
4
  import { type XfStyle } from '../style/xf-style.ts';
4
5
  import { type FormulaScope } from './formula.ts';
5
6
  /**
@@ -25,4 +26,9 @@ export interface WorksheetReadContext {
25
26
  readonly scope: FormulaScope;
26
27
  /** The workbook's date system, from `BrtWbProp`: what a serial under a date format counts from. */
27
28
  readonly dateEpoch: DateEpoch;
29
+ /** Every name the workbook defines, as `definedNameKeys` spells them: whether a function a formula
30
+ * passes as a value sheds its `_xleta.` depends on them, as it does in the XML reader. */
31
+ readonly definedNames: ReadonlySet<string>;
32
+ /** What the workbook's cell and value metadata indices resolve to, read from its metadata part. */
33
+ readonly cellMetadata: CellMetadataIndex;
28
34
  }
@@ -1,12 +1,17 @@
1
- import { encodeAddress, encodeRect, MAX_COLUMN_INDEX, MAX_ROW_INDEX } from '../../core/address.js';
1
+ import { encodeAddress, encodeRange, encodeRect, MAX_COLUMN_INDEX, MAX_ROW_INDEX, } from '../../core/address.js';
2
2
  import { coerceDateSerial } from '../../core/date.js';
3
3
  import { unmangleFunctions } from '../../core/formula.js';
4
+ import { INTERNAL } from '../../core/internal.js';
4
5
  import { assignStyleFacets } from '../../core/style.js';
6
+ import { ColumnRecordBudget, clampColumnSpan } from '../read-policy/column-budget.js';
7
+ import { admitArrayRanges, admitting } from '../read-policy/read-repair.js';
8
+ import { CellStyleResolver } from '../style/cell-style-resolution.js';
5
9
  import { applyXfToCell } from '../style/xf-style.js';
6
10
  import { decodeFormula, formulaAnchor } from './formula.js';
7
11
  import { errorCodeFor, RecordReader } from './primitives.js';
8
12
  import { readRecords } from './record-stream.js';
9
13
  import { BRT } from './record-types.js';
14
+ import { SheetProtectionRecords } from './sheet-protection.js';
10
15
  const value = (read) => ({
11
16
  kind: 'value',
12
17
  read,
@@ -14,30 +19,33 @@ const value = (read) => ({
14
19
  const formula = (read) => ({ kind: 'formula', read });
15
20
  const CELL_RECORDS = new Map([
16
21
  [BRT.CellBlank, value(() => null)],
17
- [BRT.CellRk, value((r, c) => asNumberOrDate(r.rk(), c.numFmt, c.epoch))],
18
- [BRT.CellReal, value((r, c) => asNumberOrDate(r.f64(), c.numFmt, c.epoch))],
22
+ [BRT.CellRk, value((r, c) => coerceDateSerial(r.rk(), c.numFmt, c.epoch))],
23
+ [BRT.CellReal, value((r, c) => coerceDateSerial(r.f64(), c.numFmt, c.epoch))],
19
24
  [BRT.CellBool, value((r) => r.u8() !== 0)],
20
- [BRT.CellError, value((r) => errorValueOrNull(r.u8()))],
25
+ [BRT.CellError, value((r, c) => errorOf(r.u8(), c.valueError))],
21
26
  [BRT.CellSt, value((r) => r.wideString())],
22
27
  [BRT.CellRString, value((r) => r.richString())],
23
28
  [BRT.CellIsst, value((r, c) => c.sharedStrings[r.u32()] ?? '')],
24
- [BRT.FmlaNum, formula((r, c) => asNumberOrDate(r.f64(), c.numFmt, c.epoch))],
29
+ [BRT.FmlaNum, formula((r, c) => coerceDateSerial(r.f64(), c.numFmt, c.epoch))],
25
30
  [BRT.FmlaBool, formula((r) => r.u8() !== 0)],
26
- [BRT.FmlaError, formula((r) => errorValueOrNull(r.u8()) ?? undefined)],
31
+ [BRT.FmlaError, formula((r, c) => errorOf(r.u8(), c.valueError) ?? undefined)],
27
32
  [BRT.FmlaString, formula((r) => r.wideString())],
28
33
  ]);
29
- function errorValueOrNull(code) {
30
- const error = errorCodeFor(code);
34
+ function errorOf(code, valueError) {
35
+ const error = valueError ?? errorCodeFor(code);
31
36
  return error === undefined ? null : { error };
32
37
  }
33
38
  export function parseWorksheet(part, context) {
34
- const { sheet, xfStyles, scope } = context;
39
+ const { sheet, xfStyles, scope, definedNames } = context;
35
40
  let row = -1;
36
- let rowStyle = -1;
37
- const columnStyle = new Map();
41
+ const styleResolution = new CellStyleResolver();
38
42
  let defaultRowHeight = -1;
39
43
  const groups = new Map();
40
44
  const deferred = [];
45
+ const columnBudget = new ColumnRecordBudget();
46
+ const protection = new SheetProtectionRecords();
47
+ let cellMeta = 0;
48
+ let valueMeta = 0;
41
49
  for (const record of readRecords(part)) {
42
50
  const cellRecord = CELL_RECORDS.get(record.type);
43
51
  const reader = new RecordReader(record.data);
@@ -46,59 +54,100 @@ export function parseWorksheet(part, context) {
46
54
  defaultRowHeight = reader.u16();
47
55
  }
48
56
  else if (record.type === BRT.ColInfo) {
49
- applyColumn(reader, sheet, xfStyles, columnStyle);
57
+ applyColumn(reader, sheet, xfStyles, styleResolution, columnBudget);
50
58
  }
51
59
  else if (record.type === BRT.RowHdr) {
52
60
  const header = applyRow(reader, sheet, defaultRowHeight);
53
61
  row = header.row;
54
- rowStyle = header.styleIndex;
62
+ styleResolution.openRow(header.styleIndex, header.customFormat);
55
63
  }
56
64
  else if (record.type === BRT.MergeCell) {
57
65
  const { rowFirst, rowLast, colFirst, colLast } = reader.range();
58
66
  if (inGrid(colFirst, rowFirst) && inGrid(colLast, rowLast)) {
59
- sheet.mergeCells(encodeRect({
67
+ const ref = encodeRect({
60
68
  top: rowFirst + 1,
61
69
  left: colFirst + 1,
62
70
  bottom: rowLast + 1,
63
71
  right: colLast + 1,
64
- }));
72
+ });
73
+ admitting(() => {
74
+ sheet.mergeCells(ref);
75
+ });
65
76
  }
66
77
  }
78
+ else if (record.type === BRT.SheetProtection) {
79
+ protection.legacy(reader);
80
+ }
81
+ else if (record.type === BRT.SheetProtectionIso) {
82
+ protection.iso(reader);
83
+ }
67
84
  else if (record.type === BRT.ArrFmla) {
68
- const { rowFirst, colFirst } = reader.range();
85
+ const { rowFirst, rowLast, colFirst, colLast } = reader.range();
69
86
  reader.skip(1);
87
+ const inGridRange = inGrid(colFirst, rowFirst) && inGrid(colLast, rowLast);
70
88
  groups.set(groupKey(rowFirst, colFirst), {
89
+ ref: inGridRange
90
+ ? encodeRange({
91
+ top: rowFirst + 1,
92
+ left: colFirst + 1,
93
+ bottom: rowLast + 1,
94
+ right: colLast + 1,
95
+ })
96
+ : undefined,
71
97
  rgce: reader.bytes(reader.u32()),
72
98
  rgcb: reader.bytes(reader.u32()),
73
99
  });
74
100
  }
101
+ else if (record.type === BRT.CellMeta) {
102
+ cellMeta = reader.u32();
103
+ }
104
+ else if (record.type === BRT.ValueMeta) {
105
+ valueMeta = reader.u32();
106
+ }
75
107
  else if (cellRecord !== undefined) {
76
- const member = readCellRecord(cellRecord, reader, { ...context, row, rowStyle, columnStyle });
108
+ const member = readCellRecord(cellRecord, reader, {
109
+ ...context,
110
+ row,
111
+ styleResolution,
112
+ cellMeta,
113
+ valueMeta,
114
+ });
115
+ cellMeta = 0;
116
+ valueMeta = 0;
77
117
  if (member !== undefined)
78
118
  deferred.push(member);
79
119
  }
80
120
  }
121
+ const protectedAs = protection.result();
122
+ if (protectedAs !== undefined)
123
+ sheet[INTERNAL].restoreProtection(protectedAs);
81
124
  for (const member of deferred) {
82
125
  const group = groups.get(groupKey(member.anchorRow, member.anchorColumn));
83
126
  const own = member.row === member.anchorRow && member.column === member.anchorColumn;
84
127
  member.cell.value =
85
128
  group === undefined || !own
86
129
  ? (member.result ?? null)
87
- : formulaValue(decodeFormula(group.rgce, group.rgcb, scope), member.result);
130
+ : arrayFormulaValue(decodeFormula(group.rgce, group.rgcb, scope), group.ref, member.result, definedNames, member.dynamic);
88
131
  }
132
+ admitArrayRanges(sheet);
89
133
  }
90
134
  function readCellRecord(record, reader, context) {
91
- const { sheet, sharedStrings, xfStyles, scope, dateEpoch, row, rowStyle, columnStyle } = context;
135
+ const { sheet, sharedStrings, xfStyles, scope, dateEpoch, definedNames, row, styleResolution } = context;
136
+ const { cellMetadata, cellMeta, valueMeta } = context;
92
137
  if (row <= 0)
93
138
  return undefined;
94
139
  const { column, styleIndex } = reader.cell();
95
140
  if (!inGrid(column, row - 1))
96
141
  return undefined;
97
- const resolved = styleIndex > 0 ? styleIndex : rowStyle >= 0 ? rowStyle : (columnStyle.get(column) ?? -1);
98
- const style = resolved >= 0 ? xfStyles[resolved] : xfStyles[0];
142
+ const style = styleResolution.styleFor(column + 1, styleIndex > 0 ? styleIndex : -1, xfStyles);
99
143
  const cell = sheet.getCell(encodeAddress(column + 1, row));
100
144
  applyXfToCell(cell, style);
101
- const ctx = { sharedStrings, numFmt: style?.numFmt, epoch: dateEpoch };
145
+ const ctx = {
146
+ sharedStrings,
147
+ numFmt: style?.numFmt,
148
+ epoch: dateEpoch,
149
+ valueError: cellMetadata.valueErrors.get(valueMeta),
150
+ };
102
151
  if (record.kind === 'value') {
103
152
  cell.value = record.read(reader, ctx);
104
153
  return undefined;
@@ -109,7 +158,7 @@ function readCellRecord(record, reader, context) {
109
158
  const rgcb = reader.bytes(reader.u32());
110
159
  const anchor = formulaAnchor(rgce, rgcb);
111
160
  if (anchor === undefined) {
112
- cell.value = formulaValue(decodeFormula(rgce, rgcb, scope), result);
161
+ cell.value = formulaValue(decodeFormula(rgce, rgcb, scope), result, definedNames);
113
162
  return undefined;
114
163
  }
115
164
  return {
@@ -119,23 +168,32 @@ function readCellRecord(record, reader, context) {
119
168
  anchorRow: anchor.row,
120
169
  anchorColumn: anchor.column,
121
170
  result,
171
+ dynamic: cellMetadata.dynamicArrayCells.has(cellMeta),
122
172
  };
123
173
  }
124
174
  function groupKey(row, column) {
125
175
  return `${row}:${column}`;
126
176
  }
127
- function formulaValue(formula, result) {
177
+ function formulaValue(formula, result, definedNames) {
128
178
  if (formula === undefined)
129
179
  return result ?? null;
130
- const stored = unmangleFunctions(formula);
180
+ const stored = unmangleFunctions(formula, definedNames);
131
181
  return result === undefined ? { formula: stored } : { formula: stored, result };
132
182
  }
183
+ function arrayFormulaValue(formula, ref, result, definedNames, dynamic) {
184
+ if (formula === undefined || ref === undefined)
185
+ return formulaValue(formula, result, definedNames);
186
+ return {
187
+ shareType: 'array',
188
+ formula: unmangleFunctions(formula, definedNames),
189
+ ref,
190
+ ...(dynamic ? { dynamic: true } : {}),
191
+ ...(result === undefined ? {} : { result }),
192
+ };
193
+ }
133
194
  function inGrid(column, row) {
134
195
  return column >= 0 && column <= MAX_COLUMN_INDEX && row >= 0 && row <= MAX_ROW_INDEX;
135
196
  }
136
- function asNumberOrDate(value, numFmt, epoch) {
137
- return coerceDateSerial(value, numFmt, epoch);
138
- }
139
197
  function applyRow(reader, sheet, defaultRowHeight) {
140
198
  const index = reader.u32();
141
199
  const styleIndex = reader.u32();
@@ -143,7 +201,7 @@ function applyRow(reader, sheet, defaultRowHeight) {
143
201
  reader.skip(1);
144
202
  const flags = reader.u8();
145
203
  if (index > MAX_ROW_INDEX)
146
- return { row: -1, styleIndex: -1 };
204
+ return { row: -1, styleIndex: -1, customFormat: false };
147
205
  const row = index + 1;
148
206
  const handle = sheet.getRow(row);
149
207
  if ((flags & ROW_CUSTOM_HEIGHT) !== 0 || height !== defaultRowHeight) {
@@ -156,7 +214,7 @@ function applyRow(reader, sheet, defaultRowHeight) {
156
214
  handle.outlineLevel = outlineLevel;
157
215
  if ((flags & ROW_COLLAPSED) !== 0)
158
216
  handle.collapsed = true;
159
- return { row, styleIndex: (flags & ROW_CUSTOM_FORMAT) !== 0 ? styleIndex : -1 };
217
+ return { row, styleIndex, customFormat: (flags & ROW_CUSTOM_FORMAT) !== 0 };
160
218
  }
161
219
  const TWIPS_PER_POINT = 20;
162
220
  const ROW_OUTLINE_LEVEL = 0b0000_0111;
@@ -164,7 +222,7 @@ const ROW_COLLAPSED = 0b0000_1000;
164
222
  const ROW_HIDDEN = 0b0001_0000;
165
223
  const ROW_CUSTOM_HEIGHT = 0b0010_0000;
166
224
  const ROW_CUSTOM_FORMAT = 0b0100_0000;
167
- function applyColumn(reader, sheet, xfStyles, columnStyle) {
225
+ function applyColumn(reader, sheet, xfStyles, styleResolution, budget) {
168
226
  const first = reader.u32();
169
227
  const last = reader.u32();
170
228
  const width = reader.u32();
@@ -177,11 +235,11 @@ function applyColumn(reader, sheet, xfStyles, columnStyle) {
177
235
  (flags & (COLUMN_HIDDEN | COLUMN_CUSTOM_WIDTH | COLUMN_COLLAPSED)) === 0) {
178
236
  return;
179
237
  }
180
- const lastInGrid = Math.min(last, MAX_COLUMN_INDEX);
181
- if (first > lastInGrid)
238
+ const span = clampColumnSpan(first + 1, last + 1, budget);
239
+ if (span === undefined)
182
240
  return;
183
- for (let index = first; index <= lastInGrid; index++) {
184
- const column = sheet.getColumn(index + 1);
241
+ for (let index = span.first; index <= span.last; index++) {
242
+ const column = sheet.getColumn(index);
185
243
  column.width = width / COLUMN_WIDTH_UNITS;
186
244
  if ((flags & COLUMN_HIDDEN) !== 0)
187
245
  column.hidden = true;
@@ -191,9 +249,9 @@ function applyColumn(reader, sheet, xfStyles, columnStyle) {
191
249
  column.collapsed = true;
192
250
  if (style !== undefined)
193
251
  assignStyleFacets(column, style);
194
- if (styleIndex > 0)
195
- columnStyle.set(index, styleIndex);
196
252
  }
253
+ if (styleIndex > 0)
254
+ styleResolution.noteColumnSpan(span.first, span.last, styleIndex);
197
255
  }
198
256
  const COLUMN_WIDTH_UNITS = 256;
199
257
  const COLUMN_HIDDEN = 0b0000_0001;
@@ -1,12 +1,16 @@
1
- import { unmangleFunctions } from '../../core/formula.js';
1
+ import { definedNameKeys, unmangleFunctions } from '../../core/formula.js';
2
2
  import { INTERNAL } from '../../core/internal.js';
3
3
  import { Workbook } from '../../core/workbook.js';
4
4
  import { quoted } from '../../errors.js';
5
+ import { indexCellMetadata } from '../cell-metadata/metadata.js';
6
+ import { parseRichValueErrors } from '../cell-metadata/rich-values.js';
5
7
  import { UnsupportedFormatError } from '../opc/errors.js';
6
8
  import { openSpreadsheetPackage, packageAccessors, readPartRelationships } from '../opc/read-opc.js';
9
+ import { admitting, repairedSheetNames } from '../read-policy/read-repair.js';
7
10
  import { XlsbParseError } from './errors.js';
8
11
  import { decodeFormula } from './formula.js';
9
12
  import { RecordReader } from './primitives.js';
13
+ import { parseMetadataPart } from './read-metadata.js';
10
14
  import { parseSharedStrings } from './read-shared-strings.js';
11
15
  import { parseStyleTable } from './read-styles.js';
12
16
  import { parseWorksheet } from './read-worksheet.js';
@@ -35,13 +39,16 @@ export function readXlsbPackage(files, documentPath = XLSB_WORKBOOK_PART) {
35
39
  workbook[INTERNAL].restoreDefaultFont(defaultFont);
36
40
  const declaration = readWorkbookPart(workbookPart);
37
41
  workbook.dateEpoch = declaration.dateEpoch;
42
+ const sheets = repairedSheetNames(declaration.sheets);
38
43
  const scope = {
39
- sheetNames: declaration.sheets.map((sheet) => sheet.name),
44
+ sheetNames: sheets.map((sheet) => sheet.name),
40
45
  externSheets: declaration.externSheets,
41
46
  selfSupBook: declaration.selfSupBook,
42
47
  names: declaration.names.map((name) => name.name),
43
48
  };
44
- for (const declared of declaration.sheets) {
49
+ const namesInWorkbook = definedNameKeys(declaration.names.filter(isWorkbookName));
50
+ const cellMetadata = indexCellMetadata(parseMetadataPart(rels.relatedBytes('sheetMetadata')), parseRichValueErrors(rels.relatedText('rdRichValueStructure') ?? '', rels.relatedText('rdRichValue') ?? ''));
51
+ for (const declared of sheets) {
45
52
  const sheet = workbook.addWorksheet(declared.name, { state: declared.state });
46
53
  const target = declared.relId === undefined ? undefined : rels.byId(declared.relId)?.target;
47
54
  const part = target === undefined ? undefined : partBytes(rels.pathOf(target));
@@ -52,10 +59,15 @@ export function readXlsbPackage(files, documentPath = XLSB_WORKBOOK_PART) {
52
59
  xfStyles: cellXfs,
53
60
  scope,
54
61
  dateEpoch: declaration.dateEpoch,
62
+ definedNames: namesInWorkbook,
63
+ cellMetadata,
55
64
  });
56
65
  }
57
- for (const defined of definedNames(declaration, scope))
58
- workbook.defineName(defined);
66
+ for (const defined of definedNames(declaration, scope, namesInWorkbook)) {
67
+ admitting(() => {
68
+ workbook.defineName(defined);
69
+ });
70
+ }
59
71
  return workbook;
60
72
  }
61
73
  const WORKBOOK_BLOCKS = [
@@ -138,10 +150,10 @@ function readName(data) {
138
150
  }
139
151
  const GLOBAL_NAME_SCOPE = 0xffffffff;
140
152
  const NAME_IS_FUNCTION = 0x00000002;
141
- function definedNames(declaration, scope) {
153
+ function definedNames(declaration, scope, namesInWorkbook) {
142
154
  const names = [];
143
155
  for (const declared of declaration.names) {
144
- if (declared.isFunction || declared.name === FILTER_DATABASE_NAME)
156
+ if (!isWorkbookName(declared))
145
157
  continue;
146
158
  const refersTo = decodeFormula(declared.rgce, declared.rgcb, scope);
147
159
  if (refersTo === undefined)
@@ -150,10 +162,16 @@ function definedNames(declaration, scope) {
150
162
  names.push({
151
163
  name: declared.name,
152
164
  ...(sheet === undefined ? {} : { scope: sheet }),
153
- refersTo: unmangleFunctions(refersTo),
165
+ refersTo: unmangleFunctions(refersTo, namesInWorkbook),
154
166
  });
155
167
  }
156
168
  return names;
157
169
  }
170
+ function isWorkbookName(declared) {
171
+ return (!declared.isFunction &&
172
+ !FORMULA_PLACEHOLDER_NAME.test(declared.name) &&
173
+ declared.name !== FILTER_DATABASE_NAME);
174
+ }
175
+ const FORMULA_PLACEHOLDER_NAME = /^_xl(?:fn|pm|eta)\./;
158
176
  const FILTER_DATABASE_NAME = '_xlnm._FilterDatabase';
159
177
  const SHEET_STATES = ['visible', 'hidden', 'veryHidden'];
@@ -48,4 +48,17 @@ export declare const BRT: {
48
48
  readonly ExternSheet: 362;
49
49
  readonly WsFmtInfo: 485;
50
50
  readonly ArrFmla: 426;
51
+ readonly SheetProtection: 535;
52
+ readonly SheetProtectionIso: 678;
53
+ readonly CellMeta: 49;
54
+ readonly ValueMeta: 50;
55
+ readonly Mdtinfo: 335;
56
+ readonly BeginEsfmd: 339;
57
+ readonly EndEsfmd: 340;
58
+ readonly BeginFmd: 52;
59
+ readonly BeginEsmdb: 337;
60
+ readonly EndEsmdb: 338;
61
+ readonly Mdb: 51;
62
+ readonly DynamicArrayProperties: 4097;
63
+ readonly RichValueBlock: 5003;
51
64
  };
@@ -47,4 +47,17 @@ export const BRT = {
47
47
  ExternSheet: 362,
48
48
  WsFmtInfo: 485,
49
49
  ArrFmla: 426,
50
+ SheetProtection: 535,
51
+ SheetProtectionIso: 678,
52
+ CellMeta: 49,
53
+ ValueMeta: 50,
54
+ Mdtinfo: 335,
55
+ BeginEsfmd: 339,
56
+ EndEsfmd: 340,
57
+ BeginFmd: 52,
58
+ BeginEsmdb: 337,
59
+ EndEsmdb: 338,
60
+ Mdb: 51,
61
+ DynamicArrayProperties: 4097,
62
+ RichValueBlock: 5003,
50
63
  };