reamkit 1.15.3 → 1.16.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 (95) hide show
  1. package/dist/esm/core/converter/facade.d.ts +5 -0
  2. package/dist/esm/core/converter/ream.d.ts +6 -0
  3. package/dist/esm/core/converter/ream.js +34 -8
  4. package/dist/esm/core/document-model/index.d.ts +1 -1
  5. package/dist/esm/core/document-model/types.d.ts +187 -1
  6. package/dist/esm/core/drawingml/chart-geometry.d.ts +28 -0
  7. package/dist/esm/core/drawingml/chart-geometry.js +371 -88
  8. package/dist/esm/core/drawingml/chart-parser.js +262 -11
  9. package/dist/esm/core/drawingml/colors.d.ts +6 -6
  10. package/dist/esm/core/drawingml/colors.js +58 -18
  11. package/dist/esm/core/drawingml/preset-geometry.js +7 -6
  12. package/dist/esm/core/drawingml/sparkline-geometry.js +6 -4
  13. package/dist/esm/core/drawingml/theme-parser.d.ts +41 -0
  14. package/dist/esm/core/drawingml/theme-parser.js +74 -1
  15. package/dist/esm/core/font/ttf-subset.js +26 -1
  16. package/dist/esm/core/images.js +172 -43
  17. package/dist/esm/core/indexed-colors.d.ts +11 -0
  18. package/dist/esm/core/indexed-colors.js +81 -0
  19. package/dist/esm/core/ir/sheet.d.ts +67 -3
  20. package/dist/esm/{excel → core}/number-format.d.ts +37 -0
  21. package/dist/esm/core/number-format.js +800 -0
  22. package/dist/esm/core/opc/alternate-content.d.ts +13 -0
  23. package/dist/esm/core/opc/alternate-content.js +72 -0
  24. package/dist/esm/core/opc/core-properties.js +1 -0
  25. package/dist/esm/core/opc/package.d.ts +14 -1
  26. package/dist/esm/core/opc/package.js +43 -4
  27. package/dist/esm/core/opc/relationships.js +1 -0
  28. package/dist/esm/core/opc/xml-entities.d.ts +12 -0
  29. package/dist/esm/core/opc/xml-entities.js +91 -0
  30. package/dist/esm/core/spreadsheet-model/index.d.ts +1 -1
  31. package/dist/esm/core/spreadsheet-model/types.d.ts +137 -0
  32. package/dist/esm/core/vector.d.ts +13 -0
  33. package/dist/esm/excel/activex-parser.d.ts +9 -0
  34. package/dist/esm/excel/activex-parser.js +16 -1
  35. package/dist/esm/excel/column-bands.d.ts +26 -1
  36. package/dist/esm/excel/column-bands.js +156 -20
  37. package/dist/esm/excel/comments-parser.js +5 -3
  38. package/dist/esm/excel/conditional-format.d.ts +25 -2
  39. package/dist/esm/excel/conditional-format.js +63 -19
  40. package/dist/esm/excel/escaped-text.d.ts +9 -0
  41. package/dist/esm/excel/escaped-text.js +20 -0
  42. package/dist/esm/excel/form-control-parser.js +1 -0
  43. package/dist/esm/excel/formula/dates.js +1 -1
  44. package/dist/esm/excel/header-footer.d.ts +9 -1
  45. package/dist/esm/excel/header-footer.js +162 -14
  46. package/dist/esm/excel/index.d.ts +1 -1
  47. package/dist/esm/excel/index.js +2 -2
  48. package/dist/esm/excel/pivot-table-parser.js +1 -0
  49. package/dist/esm/excel/print-model.d.ts +75 -0
  50. package/dist/esm/excel/print-model.js +902 -77
  51. package/dist/esm/excel/printer-settings.d.ts +16 -0
  52. package/dist/esm/excel/printer-settings.js +36 -0
  53. package/dist/esm/excel/rich-value.d.ts +13 -0
  54. package/dist/esm/excel/rich-value.js +120 -0
  55. package/dist/esm/excel/shared-strings-parser.js +11 -7
  56. package/dist/esm/excel/sheet-drawing.d.ts +11 -0
  57. package/dist/esm/excel/sheet-drawing.js +26 -3
  58. package/dist/esm/excel/sheet-shape-parser.d.ts +53 -1
  59. package/dist/esm/excel/sheet-shape-parser.js +461 -32
  60. package/dist/esm/excel/sheet-to-flow.d.ts +23 -2
  61. package/dist/esm/excel/sheet-to-flow.js +551 -36
  62. package/dist/esm/excel/slicer-parser.js +1 -0
  63. package/dist/esm/excel/styles-parser.d.ts +30 -2
  64. package/dist/esm/excel/styles-parser.js +177 -40
  65. package/dist/esm/excel/table-parser.js +1 -0
  66. package/dist/esm/excel/vml-drawing.d.ts +68 -0
  67. package/dist/esm/excel/vml-drawing.js +158 -0
  68. package/dist/esm/excel/workbook-parser.d.ts +5 -0
  69. package/dist/esm/excel/workbook-parser.js +5 -1
  70. package/dist/esm/excel/worksheet-parser.d.ts +2 -1
  71. package/dist/esm/excel/worksheet-parser.js +149 -28
  72. package/dist/esm/excel/xlsx-reader.js +271 -33
  73. package/dist/esm/excel/xlsx-to-pdf.js +12 -1
  74. package/dist/esm/html/html-writer.js +11 -2
  75. package/dist/esm/layout/page-doc.d.ts +26 -0
  76. package/dist/esm/layout/styled-layout.d.ts +2 -0
  77. package/dist/esm/layout/styled-layout.js +493 -64
  78. package/dist/esm/pdf/cid-font.js +20 -9
  79. package/dist/esm/pdf/objects.d.ts +13 -0
  80. package/dist/esm/pdf/objects.js +18 -1
  81. package/dist/esm/pdf/styled-page-emitter.js +164 -32
  82. package/dist/esm/pdf/text-page-renderer.js +1 -0
  83. package/dist/esm/pdf/vector-graphics.d.ts +3 -1
  84. package/dist/esm/pdf/vector-graphics.js +19 -2
  85. package/dist/esm/pptx/pptx-reader.js +1 -0
  86. package/dist/esm/pptx/slide-parser.js +12 -1
  87. package/dist/esm/svg/svg-writer.js +23 -4
  88. package/dist/esm/word/document-parser.js +1 -0
  89. package/dist/esm/word/drawing-parser.d.ts +34 -1
  90. package/dist/esm/word/drawing-parser.js +52 -2
  91. package/dist/esm/word/numbering-parser.js +1 -0
  92. package/dist/esm/word/settings-parser.js +1 -0
  93. package/dist/esm/word/styles-parser.js +1 -0
  94. package/package.json +7 -3
  95. package/dist/esm/excel/number-format.js +0 -476
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Replace every `<mc:AlternateContent>` with the markup its `<mc:Fallback>`
3
+ * holds, so the part reaching the XML parser is plain OOXML in its original
4
+ * element order. A block with no Fallback resolves to nothing — which is what
5
+ * §10.2 of the Markup Compatibility spec asks of a reader that understands
6
+ * none of the offered choices.
7
+ *
8
+ * Text with no AlternateContent is returned unchanged, which is most files.
9
+ *
10
+ * @param xml The decoded part, as read from the package.
11
+ * @returns The part with its compatibility blocks resolved.
12
+ */
13
+ export declare function resolveAlternateContent(xml: string): string;
@@ -0,0 +1,72 @@
1
+ //#region src/core/opc/alternate-content.ts
2
+ /** Deepest nesting of one AlternateContent inside another that we will unwrap. */
3
+ var MAX_DEPTH = 8;
4
+ var OPEN = /<(?:[A-Za-z_][\w.-]*:)?AlternateContent(?=[\s/>])/g;
5
+ /**
6
+ * Find the element starting at `from` (the index of its `<`) and return the
7
+ * span of its content plus the index just past its closing tag. Returns
8
+ * undefined for an unclosed element — malformed markup we leave alone.
9
+ */
10
+ function elementAt(xml, from, local) {
11
+ const nameEnd = xml.indexOf(">", from);
12
+ if (nameEnd < 0) return void 0;
13
+ if (xml[nameEnd - 1] === "/") return {
14
+ inner: "",
15
+ end: nameEnd + 1
16
+ };
17
+ const open = new RegExp(`<(?:[A-Za-z_][\\w.-]*:)?${local}(?=[\\s/>])`, "g");
18
+ const close = new RegExp(`</(?:[A-Za-z_][\\w.-]*:)?${local}\\s*>`, "g");
19
+ let depth = 1;
20
+ let cursor = nameEnd + 1;
21
+ while (depth > 0) {
22
+ close.lastIndex = cursor;
23
+ const shut = close.exec(xml);
24
+ if (!shut) return void 0;
25
+ open.lastIndex = cursor;
26
+ for (let o = open.exec(xml); o && o.index < shut.index; o = open.exec(xml)) if (xml[xml.indexOf(">", o.index) - 1] !== "/") depth++;
27
+ depth--;
28
+ cursor = shut.index + shut[0].length;
29
+ if (depth === 0) return {
30
+ inner: xml.slice(nameEnd + 1, shut.index),
31
+ end: cursor
32
+ };
33
+ }
34
+ }
35
+ /** The content of the first `<mc:Fallback>` directly inside `inner`, or ''. */
36
+ function fallbackOf(inner) {
37
+ const at = (/* @__PURE__ */ new RegExp("<(?:[A-Za-z_][\\w.-]*:)?Fallback(?=[\\s/>])")).exec(inner);
38
+ if (!at) return "";
39
+ return elementAt(inner, at.index, "Fallback")?.inner ?? "";
40
+ }
41
+ /**
42
+ * Replace every `<mc:AlternateContent>` with the markup its `<mc:Fallback>`
43
+ * holds, so the part reaching the XML parser is plain OOXML in its original
44
+ * element order. A block with no Fallback resolves to nothing — which is what
45
+ * §10.2 of the Markup Compatibility spec asks of a reader that understands
46
+ * none of the offered choices.
47
+ *
48
+ * Text with no AlternateContent is returned unchanged, which is most files.
49
+ *
50
+ * @param xml The decoded part, as read from the package.
51
+ * @returns The part with its compatibility blocks resolved.
52
+ */
53
+ function resolveAlternateContent(xml) {
54
+ let out = xml;
55
+ for (let pass = 0; pass < MAX_DEPTH; pass++) {
56
+ if (!out.includes("AlternateContent")) return out;
57
+ let cursor = 0;
58
+ let next = "";
59
+ OPEN.lastIndex = 0;
60
+ for (let found = OPEN.exec(out); found; found = OPEN.exec(out)) {
61
+ const el = elementAt(out, found.index, "AlternateContent");
62
+ if (!el) break;
63
+ next += out.slice(cursor, found.index) + fallbackOf(el.inner);
64
+ cursor = el.end;
65
+ OPEN.lastIndex = cursor;
66
+ }
67
+ out = next + out.slice(cursor);
68
+ }
69
+ return out;
70
+ }
71
+ //#endregion
72
+ export { resolveAlternateContent };
@@ -2,6 +2,7 @@ import { XMLParser } from "fast-xml-parser";
2
2
  //#region src/core/opc/core-properties.ts
3
3
  var decoder = new TextDecoder("utf-8");
4
4
  var parser = new XMLParser({
5
+ htmlEntities: true,
5
6
  ignoreAttributes: false,
6
7
  attributeNamePrefix: "@_",
7
8
  textNodeName: "#text",
@@ -28,6 +28,7 @@ export declare class OpcPackage {
28
28
  */
29
29
  private constructor();
30
30
  private readonly relsCache;
31
+ private folded;
31
32
  /**
32
33
  * Unzip and validate a package's bytes. Rejects an OLE compound file (an
33
34
  * encrypted OOXML container or a legacy binary `.doc`/`.xls`) and enforces the
@@ -37,8 +38,20 @@ export declare class OpcPackage {
37
38
  * `_rels/.rels` is missing.
38
39
  */
39
40
  static open(buffer: Uint8Array, options?: OpcOpenOptions): OpcPackage;
40
- /** The bytes of the part at `path`, or undefined when absent. */
41
+ /**
42
+ * The bytes of the part at `path`, or undefined when absent.
43
+ *
44
+ * OPC part names are compared case-insensitively (ISO/IEC 29500-2 §9.1.1.1),
45
+ * so two names differing only in case are the SAME part. Producers do mix
46
+ * them: 123233_charts.xlsx writes `xl/worksheets/Sheet1.xml` and its
47
+ * relationships as `_rels/sheet1.xml.rels`, and an exact lookup found no
48
+ * relationships for that sheet at all — which cost it four charts, silently,
49
+ * because a sheet with no drawing rel is indistinguishable from one with no
50
+ * drawing. An exact hit still wins; the fold is only a fallback.
51
+ */
41
52
  getPart(path: string): Uint8Array | undefined;
53
+ /** Parts keyed by their case-folded path, built once on first miss. */
54
+ private foldedParts;
42
55
  /**
43
56
  * The bytes of the part at `path`.
44
57
  *
@@ -19,6 +19,7 @@ var OpcPackage = class OpcPackage {
19
19
  this.rootRelationships = rootRelationships;
20
20
  }
21
21
  relsCache = /* @__PURE__ */ new Map();
22
+ folded;
22
23
  /**
23
24
  * Unzip and validate a package's bytes. Rejects an OLE compound file (an
24
25
  * encrypted OOXML container or a legacy binary `.doc`/`.xls`) and enforces the
@@ -37,7 +38,7 @@ var OpcPackage = class OpcPackage {
37
38
  let total = 0;
38
39
  let count = 0;
39
40
  let violation;
40
- const entries = unzipSync(buffer, { filter: (info) => {
41
+ const entries = unzipGuarded(buffer, { filter: (info) => {
41
42
  if (++count > maxEntries) {
42
43
  violation ??= `more than ${maxEntries} entries`;
43
44
  return false;
@@ -61,9 +62,32 @@ var OpcPackage = class OpcPackage {
61
62
  if (!relsBytes) throw new Error(`OPC package missing ${ROOT_RELS_PATH}`);
62
63
  return new OpcPackage(parts, parseRelationships(relsBytes));
63
64
  }
64
- /** The bytes of the part at `path`, or undefined when absent. */
65
+ /**
66
+ * The bytes of the part at `path`, or undefined when absent.
67
+ *
68
+ * OPC part names are compared case-insensitively (ISO/IEC 29500-2 §9.1.1.1),
69
+ * so two names differing only in case are the SAME part. Producers do mix
70
+ * them: 123233_charts.xlsx writes `xl/worksheets/Sheet1.xml` and its
71
+ * relationships as `_rels/sheet1.xml.rels`, and an exact lookup found no
72
+ * relationships for that sheet at all — which cost it four charts, silently,
73
+ * because a sheet with no drawing rel is indistinguishable from one with no
74
+ * drawing. An exact hit still wins; the fold is only a fallback.
75
+ */
65
76
  getPart(path) {
66
- return this.parts.get(normalizePath(path));
77
+ const normalized = normalizePath(path);
78
+ return this.parts.get(normalized) ?? this.foldedParts().get(normalized.toLowerCase());
79
+ }
80
+ /** Parts keyed by their case-folded path, built once on first miss. */
81
+ foldedParts() {
82
+ if (!this.folded) {
83
+ const folded = /* @__PURE__ */ new Map();
84
+ for (const [path, data] of this.parts) {
85
+ const key = path.toLowerCase();
86
+ if (!folded.has(key)) folded.set(key, data);
87
+ }
88
+ this.folded = folded;
89
+ }
90
+ return this.folded;
67
91
  }
68
92
  /**
69
93
  * The bytes of the part at `path`.
@@ -92,7 +116,7 @@ var OpcPackage = class OpcPackage {
92
116
  const dir = slash >= 0 ? normalized.substring(0, slash) : "";
93
117
  const base = slash >= 0 ? normalized.substring(slash + 1) : normalized;
94
118
  const relsPath = dir.length > 0 ? `${dir}/_rels/${base}.rels` : `_rels/${base}.rels`;
95
- const data = this.parts.get(relsPath);
119
+ const data = this.getPart(relsPath);
96
120
  const rels = data ? parseRelationships(data) : [];
97
121
  this.relsCache.set(normalized, rels);
98
122
  return rels;
@@ -162,5 +186,20 @@ function collapseDotSegments(p) {
162
186
  }
163
187
  return out.join("/");
164
188
  }
189
+ /**
190
+ * {@link unzipSync}, with a decompression failure turned into a named refusal.
191
+ *
192
+ * @param buffer The archive bytes.
193
+ * @param options The fflate options (the zip-bomb entry filter).
194
+ * @returns The decompressed entries.
195
+ * @throws Error naming the archive as invalid when inflation fails.
196
+ */
197
+ function unzipGuarded(buffer, options) {
198
+ try {
199
+ return unzipSync(buffer, options);
200
+ } catch (e) {
201
+ throw new Error(`invalid zip data: ${e instanceof Error ? e.message : String(e)}`);
202
+ }
203
+ }
165
204
  //#endregion
166
205
  export { OpcPackage };
@@ -2,6 +2,7 @@ import { XMLParser } from "fast-xml-parser";
2
2
  //#region src/core/opc/relationships.ts
3
3
  var decoder = new TextDecoder("utf-8");
4
4
  var parser = new XMLParser({
5
+ htmlEntities: true,
5
6
  ignoreAttributes: false,
6
7
  attributeNamePrefix: "@_",
7
8
  parseAttributeValue: false,
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Resolve the entities an internal DTD subset declares and remove the subset,
3
+ * so what reaches the XML parser is a well-formed part with no `&name;` left
4
+ * to leak. Markup inside an entity value is escaped to text rather than
5
+ * spliced in — an entity that opens a tag is a document we will not build.
6
+ *
7
+ * Text with no `<!DOCTYPE` is returned unchanged, which is every real file.
8
+ *
9
+ * @param xml The decoded part, as read from the package.
10
+ * @returns The part with its internal subset resolved and stripped.
11
+ */
12
+ export declare function resolveInternalEntities(xml: string): string;
@@ -0,0 +1,91 @@
1
+ //#region src/core/opc/xml-entities.ts
2
+ /** Longest text any one declared entity may expand to. */
3
+ var MAX_EXPANSION = 65536;
4
+ /** Longest total the substitutions may add to one part. */
5
+ var MAX_TOTAL = 1048576;
6
+ var DOCTYPE = /<!DOCTYPE\s[^[>]*(?:\[[\s\S]*?\]\s*)?>/;
7
+ var ENTITY_DECL = /<!ENTITY\s+([^\s%<>&;]+)\s+(?:"([^"]*)"|'([^']*)')\s*>/g;
8
+ var REFERENCE = /&([^\s;&<>]+);/g;
9
+ /** The five entities XML predefines; a declaration never overrides them. */
10
+ var PREDEFINED = new Set([
11
+ "lt",
12
+ "gt",
13
+ "amp",
14
+ "apos",
15
+ "quot"
16
+ ]);
17
+ /** Escape resolved text so it re-enters the document as characters, not markup. */
18
+ function escape(text) {
19
+ return text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
20
+ }
21
+ /**
22
+ * Substitute every entity reference in `text` using `valueOf`, leaving the
23
+ * predefined and numeric references for the XML parser. Returns undefined once
24
+ * the result passes `cap` — the caller drops what it was building.
25
+ */
26
+ function substitute(text, cap, valueOf) {
27
+ if (!text.includes("&")) return text.length > cap ? void 0 : text;
28
+ let out = "";
29
+ let last = 0;
30
+ for (const m of text.matchAll(REFERENCE)) {
31
+ const name = m[1] ?? "";
32
+ out += text.slice(last, m.index);
33
+ last = m.index + m[0].length;
34
+ out += PREDEFINED.has(name) || name.startsWith("#") ? m[0] : valueOf(name);
35
+ if (out.length > cap) return void 0;
36
+ }
37
+ out += text.slice(last);
38
+ return out.length > cap ? void 0 : out;
39
+ }
40
+ /**
41
+ * Expand each declaration once, so a name referenced ten thousand times costs
42
+ * one expansion rather than ten thousand. A cycle, an undeclared name and a
43
+ * value that outgrows MAX_EXPANSION all resolve to the empty string: XML says
44
+ * such a document is in error, and drawing the reference's own spelling is the
45
+ * one answer that is certainly wrong.
46
+ */
47
+ function expander(declared) {
48
+ const done = /* @__PURE__ */ new Map();
49
+ const active = /* @__PURE__ */ new Set();
50
+ const valueOf = (name) => {
51
+ const memo = done.get(name);
52
+ if (memo !== void 0) return memo;
53
+ if (active.has(name)) return "";
54
+ const raw = declared.get(name);
55
+ if (raw === void 0) return "";
56
+ active.add(name);
57
+ const out = substitute(raw, MAX_EXPANSION, valueOf) ?? "";
58
+ active.delete(name);
59
+ done.set(name, out);
60
+ return out;
61
+ };
62
+ return valueOf;
63
+ }
64
+ /**
65
+ * Resolve the entities an internal DTD subset declares and remove the subset,
66
+ * so what reaches the XML parser is a well-formed part with no `&name;` left
67
+ * to leak. Markup inside an entity value is escaped to text rather than
68
+ * spliced in — an entity that opens a tag is a document we will not build.
69
+ *
70
+ * Text with no `<!DOCTYPE` is returned unchanged, which is every real file.
71
+ *
72
+ * @param xml The decoded part, as read from the package.
73
+ * @returns The part with its internal subset resolved and stripped.
74
+ */
75
+ function resolveInternalEntities(xml) {
76
+ if (!xml.includes("<!DOCTYPE")) return xml;
77
+ const doctype = DOCTYPE.exec(xml);
78
+ if (!doctype) return xml;
79
+ const declared = /* @__PURE__ */ new Map();
80
+ for (const decl of doctype[0].matchAll(ENTITY_DECL)) {
81
+ const name = decl[1];
82
+ if (name === void 0 || name.startsWith("%") || PREDEFINED.has(name)) continue;
83
+ if (!declared.has(name)) declared.set(name, decl[2] ?? decl[3] ?? "");
84
+ }
85
+ const body = xml.slice(0, doctype.index) + xml.slice(doctype.index + doctype[0].length);
86
+ if (!body.includes("&")) return body;
87
+ const valueOf = expander(declared);
88
+ return substitute(body, MAX_TOTAL, (name) => escape(valueOf(name))) ?? substitute(body, MAX_TOTAL, () => "") ?? body;
89
+ }
90
+ //#endregion
91
+ export { resolveInternalEntities };
@@ -1 +1 @@
1
- export type { CellType, WorksheetCell, ColumnWidth, MergedRange, RowHeight, XlsxPageMargins, XlsxPageSetup, XlsxPrintOptions, ParsedWorksheet, XlsxFont, XlsxFill, XlsxBorderStyleName, XlsxBorderEdge, XlsxBorder, XlsxHorizontalAlign, XlsxVerticalAlign, XlsxCellAlignment, XlsxCellXf, XlsxStyles, DefinedName, Dxf, CfOperator, CfRuleCellIs, CfvoType, Cfvo, CfRuleColorScale, CfRuleDataBar, CfRuleIconSet, CfRuleTop10, CfRuleAboveAverage, CfRuleDupUnique, CfRuleText, CfRuleExpression, CfRuleTimePeriod, TimePeriodKind, CfRule, ConditionalFormat, DataValidationType, DataValidation, HyperlinkRef, HeaderFooter, FormControlRef, OleObjectRef, SheetRichRun, SparklineKind, ParsedSparkline, ExcelTable, PivotTable, SheetPane, } from './types.js';
1
+ export type { CellType, WorksheetCell, ColumnStyle, ColumnWidth, MergedRange, RowHeight, RowStyle, XlsxPageMargins, XlsxPageSetup, XlsxPrintOptions, ParsedWorksheet, XlsxFont, XlsxFill, XlsxBorderStyleName, XlsxBorderEdge, XlsxBorder, XlsxHorizontalAlign, XlsxVerticalAlign, XlsxCellAlignment, XlsxCellXf, XlsxStyles, DefinedName, Dxf, CfOperator, CfRuleCellIs, CfvoType, Cfvo, CfRuleColorScale, CfRuleDataBar, CfRuleIconSet, CfRuleTop10, CfRuleAboveAverage, CfRuleDupUnique, CfRuleText, CfRuleExpression, CfRuleTimePeriod, TimePeriodKind, CfRule, ConditionalFormat, DataValidationType, DataValidation, HyperlinkRef, HeaderFooter, FormControlRef, OleObjectRef, SheetRichRun, SparklineKind, ParsedSparkline, ExcelTable, PivotTable, SheetPane, } from './types.js';
@@ -11,12 +11,40 @@ export interface WorksheetCell {
11
11
  readonly inlineText?: string;
12
12
  /** Index into the workbook's `cellXfs` (`xl/styles.xml`). 0 means default style. */
13
13
  readonly styleIndex?: number;
14
+ /**
15
+ * §18.3.1.4 `vm` — 1-based index into `xl/metadata.xml`'s `<valueMetadata>`.
16
+ * A cell whose value is a rich value stores a legacy error in `<v>` for
17
+ * readers that predate the feature, and points here for the real one.
18
+ */
19
+ readonly valueMetadataIndex?: number;
14
20
  }
15
21
  /** §18.3.1.13 `<col>` — a width (in character units) for the column span `min..max`. */
16
22
  export interface ColumnWidth {
17
23
  readonly min: number;
18
24
  readonly max: number;
19
25
  readonly widthChars: number;
26
+ /** §18.3.1.13 `hidden` — Excel and LibreOffice print neither the column nor its cells. */
27
+ readonly hidden?: boolean;
28
+ }
29
+ /**
30
+ * §18.3.1.13 `<col style>` / §18.3.1.73 `<row s customFormat>` — the style a
31
+ * cell takes when the file gives it none of its own.
32
+ *
33
+ * A spreadsheet formats whole columns and whole rows without writing a `<c>`
34
+ * for each one: 51710.xlsx paints its column A grey with a single `<col
35
+ * min="1" max="1" style="1"/>`, and 588 of its rows carry no A cell at all.
36
+ * The band is on the page in Excel and in LibreOffice, so the default has to
37
+ * reach the paint even where there is nothing to paint it on.
38
+ */
39
+ export interface ColumnStyle {
40
+ readonly min: number;
41
+ readonly max: number;
42
+ readonly styleIndex: number;
43
+ }
44
+ /** §18.3.1.73 — the style a row hands to its unwritten cells. */
45
+ export interface RowStyle {
46
+ readonly row: number;
47
+ readonly styleIndex: number;
20
48
  }
21
49
  /** §18.3.1.55 `<mergeCell>` — a merged cell rectangle (0-indexed, inclusive bounds). */
22
50
  export interface MergedRange {
@@ -34,6 +62,8 @@ export interface RowHeight {
34
62
  readonly row: number;
35
63
  readonly heightPt: number;
36
64
  readonly customHeight: boolean;
65
+ /** §18.3.1.73 `hidden` — the row is not printed at all. */
66
+ readonly hidden?: boolean;
37
67
  }
38
68
  /** ECMA-376 Part 1 §18.3.1.62 — `<pageMargins>`. All attributes are in inches. */
39
69
  export interface XlsxPageMargins {
@@ -59,6 +89,18 @@ export interface XlsxPageSetup {
59
89
  readonly fitToWidth?: number;
60
90
  /** Number of pages tall to fit to; default 1. */
61
91
  readonly fitToHeight?: number;
92
+ /**
93
+ * `r:id` of the sheet's `printerSettings` part. When `paperSize` is absent the
94
+ * paper is whatever that part's DEVMODE says, which is how Excel records the
95
+ * choice made in the print dialog rather than in the sheet.
96
+ */
97
+ readonly printerSettingsRelId?: string;
98
+ /**
99
+ * §18.3.1.63 `cellComments` — where the sheet's notes are printed:
100
+ * `none` (the default) prints none of them, `asDisplayed` prints them where
101
+ * they float, `atEnd` gathers them after the grid.
102
+ */
103
+ readonly cellComments?: 'none' | 'asDisplayed' | 'atEnd';
62
104
  }
63
105
  /**
64
106
  * ECMA-376 Part 1 §18.3.1.70 — `<printOptions>`. Controls what is rendered when
@@ -68,6 +110,11 @@ export interface XlsxPageSetup {
68
110
  */
69
111
  export interface XlsxPrintOptions {
70
112
  readonly gridLines?: boolean;
113
+ /**
114
+ * §18.3.1.70 `headings` — print the column letters across the top and the row
115
+ * numbers down the left, the way the sheet looks on screen. Off by default.
116
+ */
117
+ readonly headings?: boolean;
71
118
  readonly horizontalCentered?: boolean;
72
119
  readonly verticalCentered?: boolean;
73
120
  }
@@ -83,6 +130,10 @@ export interface ParsedWorksheet {
83
130
  readonly columns: ReadonlyArray<ColumnWidth>;
84
131
  readonly merges: ReadonlyArray<MergedRange>;
85
132
  readonly rowHeights: ReadonlyArray<RowHeight>;
133
+ /** §18.3.1.13 `<col style>` — the default style of a whole column span. */
134
+ readonly columnStyles?: ReadonlyArray<ColumnStyle>;
135
+ /** §18.3.1.73 `<row s customFormat="1">` — the default style of a whole row. */
136
+ readonly rowStyles?: ReadonlyArray<RowStyle>;
86
137
  /**
87
138
  * ECMA-376 §18.3.1.81 `<sheetFormatPr defaultRowHeight>` — the height, in
88
139
  * points, of every row that carries no `ht` of its own. A row in a
@@ -120,6 +171,12 @@ export interface ParsedWorksheet {
120
171
  readonly colBreaks?: ReadonlyArray<number>;
121
172
  /** §18.3.1.36 `<drawing r:id>` — the sheet's drawing part (charts/shapes). */
122
173
  readonly drawingRelId?: string;
174
+ /**
175
+ * §18.3.1.36 `<legacyDrawing r:id>` — the relationship to the sheet's VML
176
+ * drawing part. A form control put on the sheet by Excel's Forms toolbar is
177
+ * declared there and nowhere else.
178
+ */
179
+ readonly legacyDrawingRelId?: string;
123
180
  /** §18.3.1.18 `<conditionalFormatting>` — value-driven cell formats (E-SHEET SC1). */
124
181
  readonly conditionalFormats?: ReadonlyArray<ConditionalFormat>;
125
182
  /**
@@ -254,6 +311,8 @@ export interface XlsxFont {
254
311
  readonly bold?: boolean;
255
312
  readonly italic?: boolean;
256
313
  readonly underline?: boolean;
314
+ /** §18.8.37 `<strike/>` — the font is struck through. */
315
+ readonly strike?: boolean;
257
316
  readonly colorHex?: string;
258
317
  readonly name?: string;
259
318
  }
@@ -341,6 +400,18 @@ export interface XlsxStyles {
341
400
  export interface Dxf {
342
401
  readonly font?: XlsxFont;
343
402
  readonly fill?: XlsxFill;
403
+ /**
404
+ * §18.8.9 `<border>` inside a dxf — a rule may format a cell with nothing but
405
+ * an edge, and half the differential formats in a real workbook do exactly
406
+ * that (a rule under every year boundary, a box around a total).
407
+ */
408
+ readonly border?: XlsxBorder;
409
+ /**
410
+ * §18.8.9 `<numFmt formatCode>` — a rule may change how the VALUE reads, not
411
+ * just how the cell looks: two decimals where a threshold is crossed. Every
412
+ * dxf in new_cond_format_test.xlsx is one of these and nothing else.
413
+ */
414
+ readonly numberFormat?: string;
344
415
  }
345
416
  /**
346
417
  * ECMA-376 Part 1 §18.2.5 — `<definedName>`. A workbook-scoped (or sheet-scoped,
@@ -364,6 +435,12 @@ export interface CfRuleCellIs {
364
435
  readonly operator: CfOperator;
365
436
  readonly formulas: ReadonlyArray<string>;
366
437
  readonly dxfId: number;
438
+ /**
439
+ * The format itself, when the rule carries it instead of pointing at the
440
+ * workbook's table — the 2009 extension (`<x14:cfRule><x14:dxf>`) writes it
441
+ * inline, and `dxfId` names nothing there. Takes precedence when present.
442
+ */
443
+ readonly dxf?: Dxf;
367
444
  }
368
445
  /**
369
446
  * §18.3.1.11 ST_CfvoType — how a `<cfvo>` stop's threshold is derived.
@@ -406,6 +483,12 @@ export interface CfRuleDataBar {
406
483
  readonly colorHex: string;
407
484
  readonly minLength?: number;
408
485
  readonly maxLength?: number;
486
+ /**
487
+ * §18.3.1.28 `showValue` — false means the cell shows the BAR ONLY, with its
488
+ * number hidden. Excel's "Show Bar Only" checkbox; a dashboard uses it so the
489
+ * figure does not sit on top of its own gauge.
490
+ */
491
+ readonly showValue?: boolean;
409
492
  }
410
493
  /**
411
494
  * §18.3.1.49 `<cfRule type="iconSet">` — picks one glyph per cell from a named
@@ -432,6 +515,12 @@ export interface CfRuleTop10 {
432
515
  readonly percent: boolean;
433
516
  readonly bottom: boolean;
434
517
  readonly dxfId: number;
518
+ /**
519
+ * The format itself, when the rule carries it instead of pointing at the
520
+ * workbook's table — the 2009 extension (`<x14:cfRule><x14:dxf>`) writes it
521
+ * inline, and `dxfId` names nothing there. Takes precedence when present.
522
+ */
523
+ readonly dxf?: Dxf;
435
524
  }
436
525
  /**
437
526
  * §18.3.1.10 `<cfRule type="aboveAverage">` — cells above (default) or below the
@@ -445,6 +534,12 @@ export interface CfRuleAboveAverage {
445
534
  readonly equalAverage: boolean;
446
535
  readonly stdDev?: number;
447
536
  readonly dxfId: number;
537
+ /**
538
+ * The format itself, when the rule carries it instead of pointing at the
539
+ * workbook's table — the 2009 extension (`<x14:cfRule><x14:dxf>`) writes it
540
+ * inline, and `dxfId` names nothing there. Takes precedence when present.
541
+ */
542
+ readonly dxf?: Dxf;
448
543
  }
449
544
  /**
450
545
  * §18.3.1.10 `<cfRule type="duplicateValues" | "uniqueValues">` — cells whose
@@ -456,6 +551,12 @@ export interface CfRuleDupUnique {
456
551
  readonly type: 'duplicateValues' | 'uniqueValues';
457
552
  readonly priority: number;
458
553
  readonly dxfId: number;
554
+ /**
555
+ * The format itself, when the rule carries it instead of pointing at the
556
+ * workbook's table — the 2009 extension (`<x14:cfRule><x14:dxf>`) writes it
557
+ * inline, and `dxfId` names nothing there. Takes precedence when present.
558
+ */
559
+ readonly dxf?: Dxf;
459
560
  }
460
561
  /**
461
562
  * §18.3.1.10 `<cfRule type="containsText" | "notContainsText" | "beginsWith" |
@@ -468,6 +569,12 @@ export interface CfRuleText {
468
569
  readonly priority: number;
469
570
  readonly text: string;
470
571
  readonly dxfId: number;
572
+ /**
573
+ * The format itself, when the rule carries it instead of pointing at the
574
+ * workbook's table — the 2009 extension (`<x14:cfRule><x14:dxf>`) writes it
575
+ * inline, and `dxfId` names nothing there. Takes precedence when present.
576
+ */
577
+ readonly dxf?: Dxf;
471
578
  readonly formula?: string;
472
579
  }
473
580
  /**
@@ -483,6 +590,12 @@ export interface CfRuleExpression {
483
590
  readonly priority: number;
484
591
  readonly formula: string;
485
592
  readonly dxfId: number;
593
+ /**
594
+ * The format itself, when the rule carries it instead of pointing at the
595
+ * workbook's table — the 2009 extension (`<x14:cfRule><x14:dxf>`) writes it
596
+ * inline, and `dxfId` names nothing there. Takes precedence when present.
597
+ */
598
+ readonly dxf?: Dxf;
486
599
  }
487
600
  /** §18.18.82 ST_TimePeriod — the clock-relative window a `timePeriod` rule tests. */
488
601
  export type TimePeriodKind = 'today' | 'yesterday' | 'tomorrow' | 'last7Days' | 'thisWeek' | 'lastWeek' | 'nextWeek' | 'thisMonth' | 'lastMonth' | 'nextMonth';
@@ -499,6 +612,12 @@ export interface CfRuleTimePeriod {
499
612
  readonly priority: number;
500
613
  readonly timePeriod: TimePeriodKind;
501
614
  readonly dxfId: number;
615
+ /**
616
+ * The format itself, when the rule carries it instead of pointing at the
617
+ * workbook's table — the 2009 extension (`<x14:cfRule><x14:dxf>`) writes it
618
+ * inline, and `dxfId` names nothing there. Takes precedence when present.
619
+ */
620
+ readonly dxf?: Dxf;
502
621
  readonly formula?: string;
503
622
  }
504
623
  /** One conditional-format rule (`<cfRule>`): the union over the supported `type`s. */
@@ -542,6 +661,18 @@ export interface HeaderFooter {
542
661
  export interface FormControlRef {
543
662
  readonly name?: string;
544
663
  readonly relId: string;
664
+ /**
665
+ * §18.3.1.19 `@shapeId` — the id of this control's shape in the sheet's legacy
666
+ * VML drawing. It is what tells an ActiveX control's VML shape apart from a
667
+ * Forms-toolbar control that has no `<control>` entry at all.
668
+ */
669
+ readonly shapeId?: string;
670
+ /**
671
+ * §18.3.1.20 `<controlPr print>` — Excel's "Print object" checkbox. It
672
+ * defaults to true, and a control that clears it is on screen only: neither
673
+ * Excel nor Calc puts it on the page.
674
+ */
675
+ readonly print?: boolean;
545
676
  }
546
677
  /**
547
678
  * §18.3.* `<oleObjects><oleObject progId r:id>` — an embedded OLE / ActiveX
@@ -567,6 +698,12 @@ export interface SheetRichRun {
567
698
  readonly bold?: boolean;
568
699
  readonly italic?: boolean;
569
700
  readonly underline?: boolean;
701
+ /**
702
+ * §18.8.37 `<strike/>` on the run — struck-through text inside a cell that is
703
+ * otherwise not. 58315.xlsx crosses out the middle of "320-338 350", which is
704
+ * the whole point of the cell.
705
+ */
706
+ readonly strike?: boolean;
570
707
  readonly colorHex?: string;
571
708
  readonly sizePt?: number;
572
709
  /** §18.4.2 `<vertAlign>` — superscript / subscript within the cell text. */
@@ -82,6 +82,19 @@ export interface VectorShape {
82
82
  readonly fillGradient?: ShapeGradient;
83
83
  /** Stroke description. Omitted = no stroke. */
84
84
  readonly stroke?: StrokeStyle;
85
+ /**
86
+ * §20.1.8.40 — a drop shadow drawn UNDER this shape: the same paths, offset
87
+ * by `(dxPt, dyPt)` in the page's own frame (y down), filled in `colorHex` at
88
+ * `alpha`. `blurPt` is the softness the source asked for; writers that cannot
89
+ * blur draw a hard edge.
90
+ */
91
+ readonly shadow?: {
92
+ readonly dxPt: number;
93
+ readonly dyPt: number;
94
+ readonly blurPt: number;
95
+ readonly colorHex: string;
96
+ readonly alpha: number;
97
+ };
85
98
  /**
86
99
  * CTM applied via `cm` (§8.3.4): maps the local frame onto the page. The
87
100
  * 6-tuple is `[a b c d e f]` of the matrix `[[a b 0][c d 0][e f 1]]`.
@@ -22,6 +22,15 @@ export declare function parseActiveX(data: Uint8Array): ActiveXProps;
22
22
  * unknown progId falls back to a generic `'control'`.
23
23
  */
24
24
  export declare function activeXType(progId: string | undefined): string;
25
+ /**
26
+ * The affordance key for an `<ax:ocx ax:classid>`, for controls reached through
27
+ * §18.3.1.19 `<control>` rather than `<oleObject progId>` — the `<control>`
28
+ * element carries no progId, so the class id is all there is to type it by.
29
+ *
30
+ * @param xmlData The `activeX#.xml` part bytes.
31
+ * @returns The affordance key, or `'control'` when the class id is unknown.
32
+ */
33
+ export declare function activeXTypeFromPart(xmlData: Uint8Array): string;
25
34
  /**
26
35
  * The `<ax:ocx r:id>` of a control whose state is persisted to a binary stream
27
36
  * (`persistStreamInit` / `persistStream` / `persistStorage`) rather than to
@@ -2,6 +2,7 @@ import { XMLParser } from "fast-xml-parser";
2
2
  //#region src/excel/activex-parser.ts
3
3
  var decoder = new TextDecoder("utf-8");
4
4
  var parser = new XMLParser({
5
+ htmlEntities: true,
5
6
  ignoreAttributes: false,
6
7
  attributeNamePrefix: "@_",
7
8
  parseAttributeValue: false,
@@ -62,6 +63,20 @@ function strAttr(obj, key) {
62
63
  const v = obj[`@_${key}`];
63
64
  return typeof v === "string" ? v : void 0;
64
65
  }
66
+ var ACTIVEX_CLASS_TYPES = new Map([["8BD21D40-EC42-11CE-9E0D-00AA006002F3", "checkbox"], ["8BD21D50-EC42-11CE-9E0D-00AA006002F3", "option"]]);
67
+ /**
68
+ * The affordance key for an `<ax:ocx ax:classid>`, for controls reached through
69
+ * §18.3.1.19 `<control>` rather than `<oleObject progId>` — the `<control>`
70
+ * element carries no progId, so the class id is all there is to type it by.
71
+ *
72
+ * @param xmlData The `activeX#.xml` part bytes.
73
+ * @returns The affordance key, or `'control'` when the class id is unknown.
74
+ */
75
+ function activeXTypeFromPart(xmlData) {
76
+ const ocx = asObject(parser.parse(decoder.decode(xmlData))["ocx"]);
77
+ const key = (ocx ? strAttr(ocx, "classid") : void 0)?.replace(/[{}]/g, "").toUpperCase() ?? "";
78
+ return ACTIVEX_CLASS_TYPES.get(key) ?? "control";
79
+ }
65
80
  /**
66
81
  * The `<ax:ocx r:id>` of a control whose state is persisted to a binary stream
67
82
  * (`persistStreamInit` / `persistStream` / `persistStorage`) rather than to
@@ -209,4 +224,4 @@ function parseActiveXBin(data) {
209
224
  };
210
225
  }
211
226
  //#endregion
212
- export { activeXBinRelId, activeXType, parseActiveX, parseActiveXBin };
227
+ export { activeXBinRelId, activeXType, activeXTypeFromPart, parseActiveX, parseActiveXBin };