reamkit 1.15.4 → 1.17.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 (127) hide show
  1. package/dist/esm/core/bidi/segments.js +1 -2
  2. package/dist/esm/core/converter/facade.d.ts +5 -0
  3. package/dist/esm/core/converter/project.js +5 -1
  4. package/dist/esm/core/converter/ream.d.ts +6 -0
  5. package/dist/esm/core/converter/ream.js +34 -8
  6. package/dist/esm/core/document-model/index.d.ts +1 -1
  7. package/dist/esm/core/document-model/types.d.ts +659 -7
  8. package/dist/esm/core/drawingml/chart-geometry.d.ts +34 -10
  9. package/dist/esm/core/drawingml/chart-geometry.js +424 -92
  10. package/dist/esm/core/drawingml/chart-parser.js +301 -11
  11. package/dist/esm/core/drawingml/colors.d.ts +7 -7
  12. package/dist/esm/core/drawingml/colors.js +84 -18
  13. package/dist/esm/core/drawingml/preset-geometry.js +240 -6
  14. package/dist/esm/core/drawingml/shape-render.d.ts +12 -0
  15. package/dist/esm/core/drawingml/shape-render.js +116 -3
  16. package/dist/esm/core/drawingml/sparkline-geometry.js +6 -4
  17. package/dist/esm/core/drawingml/theme-parser.d.ts +51 -0
  18. package/dist/esm/core/drawingml/theme-parser.js +92 -1
  19. package/dist/esm/core/font/measure.d.ts +5 -0
  20. package/dist/esm/core/font/measure.js +21 -1
  21. package/dist/esm/core/font/ttf-subset.js +26 -1
  22. package/dist/esm/core/images.d.ts +2 -2
  23. package/dist/esm/core/images.js +353 -43
  24. package/dist/esm/core/indexed-colors.d.ts +11 -0
  25. package/dist/esm/core/indexed-colors.js +81 -0
  26. package/dist/esm/core/ir/flow.d.ts +23 -1
  27. package/dist/esm/core/ir/sheet.d.ts +61 -0
  28. package/dist/esm/{excel → core}/number-format.d.ts +37 -0
  29. package/dist/esm/core/number-format.js +800 -0
  30. package/dist/esm/core/numbering/apply.d.ts +5 -3
  31. package/dist/esm/core/numbering/apply.js +42 -11
  32. package/dist/esm/core/numbering/index.js +2 -0
  33. package/dist/esm/core/numbering/state.d.ts +13 -1
  34. package/dist/esm/core/numbering/state.js +256 -15
  35. package/dist/esm/core/opc/alternate-content.d.ts +13 -0
  36. package/dist/esm/core/opc/alternate-content.js +72 -0
  37. package/dist/esm/core/opc/core-properties.js +1 -0
  38. package/dist/esm/core/opc/package.d.ts +14 -1
  39. package/dist/esm/core/opc/package.js +43 -4
  40. package/dist/esm/core/opc/relationships.js +1 -0
  41. package/dist/esm/core/opc/xml-entities.d.ts +12 -0
  42. package/dist/esm/core/opc/xml-entities.js +91 -0
  43. package/dist/esm/core/po-helpers.js +16 -1
  44. package/dist/esm/core/spreadsheet-model/index.d.ts +1 -1
  45. package/dist/esm/core/spreadsheet-model/types.d.ts +137 -0
  46. package/dist/esm/core/style-cascade/resolver.js +66 -11
  47. package/dist/esm/core/style-cascade/table.js +35 -11
  48. package/dist/esm/core/style-cascade/types.d.ts +42 -1
  49. package/dist/esm/core/style-cascade/types.js +4 -0
  50. package/dist/esm/core/vector.d.ts +30 -0
  51. package/dist/esm/excel/activex-parser.js +1 -0
  52. package/dist/esm/excel/column-bands.d.ts +26 -1
  53. package/dist/esm/excel/column-bands.js +156 -20
  54. package/dist/esm/excel/comments-parser.js +5 -3
  55. package/dist/esm/excel/conditional-format.d.ts +25 -2
  56. package/dist/esm/excel/conditional-format.js +63 -19
  57. package/dist/esm/excel/escaped-text.d.ts +9 -0
  58. package/dist/esm/excel/escaped-text.js +20 -0
  59. package/dist/esm/excel/form-control-parser.js +1 -0
  60. package/dist/esm/excel/formula/dates.js +1 -1
  61. package/dist/esm/excel/header-footer.d.ts +9 -1
  62. package/dist/esm/excel/header-footer.js +162 -14
  63. package/dist/esm/excel/index.d.ts +1 -1
  64. package/dist/esm/excel/index.js +2 -2
  65. package/dist/esm/excel/pivot-table-parser.js +1 -0
  66. package/dist/esm/excel/print-model.d.ts +75 -0
  67. package/dist/esm/excel/print-model.js +899 -77
  68. package/dist/esm/excel/printer-settings.d.ts +16 -0
  69. package/dist/esm/excel/printer-settings.js +36 -0
  70. package/dist/esm/excel/rich-value.d.ts +13 -0
  71. package/dist/esm/excel/rich-value.js +120 -0
  72. package/dist/esm/excel/shared-strings-parser.js +11 -7
  73. package/dist/esm/excel/sheet-drawing.d.ts +11 -0
  74. package/dist/esm/excel/sheet-drawing.js +26 -3
  75. package/dist/esm/excel/sheet-shape-parser.d.ts +53 -1
  76. package/dist/esm/excel/sheet-shape-parser.js +461 -32
  77. package/dist/esm/excel/sheet-to-flow.d.ts +23 -2
  78. package/dist/esm/excel/sheet-to-flow.js +553 -36
  79. package/dist/esm/excel/slicer-parser.js +1 -0
  80. package/dist/esm/excel/styles-parser.d.ts +30 -2
  81. package/dist/esm/excel/styles-parser.js +177 -40
  82. package/dist/esm/excel/table-parser.js +1 -0
  83. package/dist/esm/excel/vml-drawing.d.ts +68 -0
  84. package/dist/esm/excel/vml-drawing.js +158 -0
  85. package/dist/esm/excel/workbook-parser.d.ts +5 -0
  86. package/dist/esm/excel/workbook-parser.js +5 -1
  87. package/dist/esm/excel/worksheet-parser.d.ts +2 -1
  88. package/dist/esm/excel/worksheet-parser.js +150 -61
  89. package/dist/esm/excel/xlsx-reader.js +242 -11
  90. package/dist/esm/excel/xlsx-to-pdf.js +12 -1
  91. package/dist/esm/html/html-writer.js +29 -6
  92. package/dist/esm/layout/page-doc.d.ts +87 -1
  93. package/dist/esm/layout/styled-layout.d.ts +46 -2
  94. package/dist/esm/layout/styled-layout.js +2345 -274
  95. package/dist/esm/pdf/cid-font.js +20 -9
  96. package/dist/esm/pdf/objects.d.ts +13 -0
  97. package/dist/esm/pdf/objects.js +18 -1
  98. package/dist/esm/pdf/shading.js +76 -6
  99. package/dist/esm/pdf/styled-page-emitter.js +355 -49
  100. package/dist/esm/pdf/text-page-renderer.js +1 -0
  101. package/dist/esm/pdf/vector-graphics.d.ts +31 -2
  102. package/dist/esm/pdf/vector-graphics.js +103 -3
  103. package/dist/esm/pptx/pptx-reader.js +2 -0
  104. package/dist/esm/pptx/slide-parser.d.ts +21 -4
  105. package/dist/esm/pptx/slide-parser.js +98 -24
  106. package/dist/esm/svg/svg-writer.js +23 -4
  107. package/dist/esm/word/document-parser.d.ts +96 -7
  108. package/dist/esm/word/document-parser.js +538 -116
  109. package/dist/esm/word/docx-reader.js +135 -43
  110. package/dist/esm/word/docx-writer.js +6 -1
  111. package/dist/esm/word/drawing-parser.d.ts +86 -5
  112. package/dist/esm/word/drawing-parser.js +1590 -54
  113. package/dist/esm/word/index.d.ts +4 -4
  114. package/dist/esm/word/numbering-parser.d.ts +2 -1
  115. package/dist/esm/word/numbering-parser.js +108 -21
  116. package/dist/esm/word/paragraph-properties.d.ts +11 -1
  117. package/dist/esm/word/paragraph-properties.js +219 -10
  118. package/dist/esm/word/run-properties.js +45 -1
  119. package/dist/esm/word/settings-parser.d.ts +24 -0
  120. package/dist/esm/word/settings-parser.js +32 -5
  121. package/dist/esm/word/shading.d.ts +11 -0
  122. package/dist/esm/word/shading.js +64 -0
  123. package/dist/esm/word/styles-parser.js +9 -2
  124. package/dist/esm/word/table-parser.d.ts +9 -1
  125. package/dist/esm/word/table-parser.js +182 -30
  126. package/package.json +7 -3
  127. package/dist/esm/excel/number-format.js +0 -476
@@ -1,6 +1,7 @@
1
1
  import { emuToPt, pt } from "../core/ir/units.js";
2
- import { poChildren, poFirstChild, poIntAttr, poIs, poText } from "../core/po-helpers.js";
3
- import { parseFill, parseLine } from "../word/drawing-parser.js";
2
+ import { poAttr, poChildren, poFirstChild, poIntAttr, poIs, poTag, poText } from "../core/po-helpers.js";
3
+ import { applyColorMods, resolveColorNode } from "../core/drawingml/colors.js";
4
+ import { parseFill, parseLine, parseShadow, parseXfrm, shadowFromOuterShdw } from "../word/drawing-parser.js";
4
5
  import { parseGeometry, parseTxBody } from "../pptx/slide-parser.js";
5
6
  import { parseXml } from "../pptx/pptx-reader.js";
6
7
  import { makeColWidthPt, makeRowHeightPt } from "./sheet-drawing.js";
@@ -11,6 +12,65 @@ var ANCHOR_KINDS = [
11
12
  "xdr:absoluteAnchor"
12
13
  ];
13
14
  /**
15
+ * One `xdr:sp` / `dsp:sp` as a {@link ShapeBlock} in the box it occupies, or
16
+ * undefined when it would draw nothing at all.
17
+ *
18
+ * @param sp The shape element.
19
+ * @param box The box it occupies on the page.
20
+ * @param ns Which namespace its own children carry.
21
+ * @param colors The theme colour resolver.
22
+ * @param themeLineWidths `a:lnStyleLst` widths, indexed by `<a:lnRef idx>`.
23
+ * @param themeFillStyles `a:fillStyleLst` nodes, indexed by `<a:fillRef idx>`.
24
+ * @param themeEffectStyles `a:effectStyleLst` nodes, indexed by `<a:effectRef idx>`.
25
+ */
26
+ function buildShape(sp, box, ns, colors, themeLineWidths, themeFillStyles, themeEffectStyles) {
27
+ const spPr = poChildren(sp).find((c) => poIs(c, `${ns}:spPr`));
28
+ const txBody = poChildren(sp).find((c) => poIs(c, `${ns}:txBody`));
29
+ const geometry = parseGeometry(spPr);
30
+ const parsed = txBody ? parseTxBody(txBody, void 0, void 0, colors, void 0) : void 0;
31
+ const lettered = (parsed?.content ?? []).some((b) => b.kind === "paragraph" && b.paragraph.runs.some((r) => r.text.trim().length > 0));
32
+ const xfrm = spPr ? poChildren(spPr).find((c) => poIs(c, "a:xfrm")) : void 0;
33
+ const transform = xfrm ? parseXfrm(xfrm) : void 0;
34
+ const own = transform !== void 0 && Object.keys(transform).length > 0 && xfrm && !lettered ? poChildren(xfrm).find((c) => poIs(c, "a:ext")) : void 0;
35
+ const ownW = own ? emuToPt(poIntAttr(own, "cx") ?? 0) : 0;
36
+ const ownH = own ? emuToPt(poIntAttr(own, "cy") ?? 0) : 0;
37
+ const useOwn = ownW > 0 && ownH > 0;
38
+ const widthPt = useOwn ? ownW : box.widthPt;
39
+ const heightPt = useOwn ? ownH : box.heightPt;
40
+ const xPt = useOwn ? box.xPt + (box.widthPt - ownW) / 2 : box.xPt;
41
+ const yPt = useOwn ? box.yPt + (box.heightPt - ownH) / 2 : box.yPt;
42
+ const spFill = spPr ? parseFill(spPr, colors) : { kind: "none" };
43
+ const fill = spFill.kind === "none" ? styleFill(sp, ns, colors, themeFillStyles) : spFill;
44
+ const directLine = spPr ? parseLine(spPr, colors) : void 0;
45
+ const line = directLine && Object.keys(directLine).length > 0 ? directLine : styleLine(sp, ns, colors, themeLineWidths);
46
+ const text = parsed ? withStyleTextColor(parsed, sp, ns, colors) : void 0;
47
+ const shadow = (spPr ? parseShadow(spPr, colors) : void 0) ?? styleShadow(sp, ns, colors, themeEffectStyles);
48
+ const visibleLine = line !== void 0 && line.fill !== "none";
49
+ if (!text && fill.kind === "none" && !visibleLine) return void 0;
50
+ return {
51
+ float: {
52
+ wrap: "none",
53
+ posH: {
54
+ relativeFrom: "margin",
55
+ offsetPt: pt(xPt)
56
+ },
57
+ posV: {
58
+ relativeFrom: "margin",
59
+ offsetPt: pt(yPt)
60
+ }
61
+ },
62
+ width: pt(widthPt),
63
+ height: pt(heightPt),
64
+ geometry,
65
+ fill,
66
+ ...transform && Object.keys(transform).length > 0 ? { transform } : {},
67
+ ...line ? { line } : {},
68
+ ...text ? { text } : {},
69
+ ...shadow ? { shadow } : {},
70
+ paragraphProperties: {}
71
+ };
72
+ }
73
+ /**
14
74
  * Parse a drawing's `xdr:sp` shape anchors (§20.5.2.30) into anchor-ordered
15
75
  * {@link ShapeBlock}s (E-SHEET W2), reusing the shared DrawingML geometry / fill /
16
76
  * line / text readers. A shape's box comes from its sheet anchor (from/to tracks),
@@ -21,8 +81,14 @@ var ANCHOR_KINDS = [
21
81
  * preserveOrder tree the shape readers expect).
22
82
  * @param worksheet The host worksheet, for the column/row track geometry.
23
83
  * @param colors The theme colour resolver threaded into fill/line parsing.
84
+ * @param themeLineWidths The theme's `a:lnStyleLst` widths in points, which an
85
+ * `<a:lnRef idx>` indexes for a gallery-styled outline.
86
+ * @param themeFillStyles The theme's `a:fillStyleLst` nodes, which an
87
+ * `<a:fillRef idx>` indexes for a gallery-styled fill.
88
+ * @param themeEffectStyles The theme's `a:effectStyleLst` nodes, which an
89
+ * `<a:effectRef idx>` indexes for a gallery-styled shadow.
24
90
  */
25
- function parseSheetShapes(drawingXml, worksheet, colors) {
91
+ function parseSheetShapes(drawingXml, worksheet, colors, themeLineWidths = [], themeFillStyles = [], themeEffectStyles = []) {
26
92
  const wsDr = parseXml(drawingXml).find((n) => poIs(n, "xdr:wsDr"));
27
93
  if (!wsDr) return [];
28
94
  const colWidthPt = makeColWidthPt(worksheet);
@@ -30,33 +96,361 @@ function parseSheetShapes(drawingXml, worksheet, colors) {
30
96
  const shapes = [];
31
97
  for (const anchor of poChildren(wsDr)) {
32
98
  if (!ANCHOR_KINDS.some((k) => poIs(anchor, k))) continue;
33
- const sp = poChildren(anchor).find((c) => poIs(c, "xdr:sp"));
34
- if (!sp) continue;
99
+ const anchored = anchorBox(anchor, colWidthPt, rowHeightPt);
100
+ if (!anchored) continue;
101
+ const placed = [];
102
+ const direct = poChildren(anchor).find((c) => poIs(c, "xdr:sp"));
103
+ if (direct) placed.push({
104
+ sp: direct,
105
+ box: anchored
106
+ });
107
+ for (const group of poChildren(anchor).filter((c) => poIs(c, "xdr:grpSp"))) collectGrouped(group, anchored, placed);
108
+ for (const { sp, box } of placed) {
109
+ if (isHiddenDrawing(sp, "xdr:nvSpPr")) continue;
110
+ const shape = buildShape(sp, box, "xdr", colors, themeLineWidths, themeFillStyles, themeEffectStyles);
111
+ if (!shape) continue;
112
+ shapes.push({
113
+ shape,
114
+ anchorRow: box.anchorRow
115
+ });
116
+ }
117
+ }
118
+ shapes.sort((a, b) => a.anchorRow - b.anchorRow);
119
+ return shapes.map((s) => s.shape);
120
+ }
121
+ /**
122
+ * The boxes the diagram frames of a drawing occupy, in document order.
123
+ *
124
+ * §20.5.2.16 `xdr:graphicFrame` is how a sheet hosts a diagram: the frame is
125
+ * anchored like any other drawing and the diagram itself lives in its own
126
+ * parts. The frame gives the corner its fallback shapes are laid out from —
127
+ * see {@link parseDiagramShapes}.
128
+ *
129
+ * @param drawingXml The drawing part bytes.
130
+ * @param worksheet The host worksheet, for the column/row track geometry.
131
+ * @returns One box per diagram frame, in the order the drawing writes them.
132
+ */
133
+ function parseDiagramFrames(drawingXml, worksheet) {
134
+ const wsDr = parseXml(drawingXml).find((n) => poIs(n, "xdr:wsDr"));
135
+ if (!wsDr) return [];
136
+ const colWidthPt = makeColWidthPt(worksheet);
137
+ const rowHeightPt = makeRowHeightPt(worksheet);
138
+ const out = [];
139
+ for (const anchor of poChildren(wsDr)) {
140
+ if (!ANCHOR_KINDS.some((k) => poIs(anchor, k))) continue;
141
+ const frame = poChildren(anchor).find((c) => poIs(c, "xdr:graphicFrame"));
142
+ if (!frame) continue;
143
+ if (!poFirstChild(poFirstChild(poFirstChild(frame, "a:graphic"), "a:graphicData"), "dgm:relIds")) continue;
35
144
  const box = anchorBox(anchor, colWidthPt, rowHeightPt);
145
+ if (box) out.push(box);
146
+ }
147
+ return out;
148
+ }
149
+ /**
150
+ * Parse the fallback drawing of a SmartArt diagram (§ MS-ODRAWXML 2.1
151
+ * `dsp:drawing`) into the shapes it lays out.
152
+ *
153
+ * A diagram is FOUR parts of description — data, layout, quick style, colours —
154
+ * that a renderer is meant to lay out itself, and nobody outside Office does.
155
+ * The producer therefore also writes what it drew: a plain DrawingML picture of
156
+ * the result, under `dsp:`, reachable from the drawing part by a
157
+ * `diagramDrawing` relationship. Reading it is the difference between a diagram
158
+ * and a blank space — tdf83671_SmartArt_import.xlsx draws three nested circles
159
+ * where we drew nothing between its "start" and its "end".
160
+ *
161
+ * The shapes' `a:xfrm` offsets are relative to the frame the diagram sits in,
162
+ * so the frame's own corner is all that has to be added.
163
+ *
164
+ * @param diagramXml The `dsp:drawing` part bytes.
165
+ * @param frame The box the graphic frame occupies on the page.
166
+ * @param colors The theme colour resolver.
167
+ * @param themeLineWidths `a:lnStyleLst` widths, indexed by `<a:lnRef idx>`.
168
+ * @param themeFillStyles `a:fillStyleLst` nodes, indexed by `<a:fillRef idx>`.
169
+ * @param themeEffectStyles `a:effectStyleLst` nodes, indexed by `<a:effectRef idx>`.
170
+ * @returns The shapes, in the order the diagram stacks them.
171
+ */
172
+ function parseDiagramShapes(diagramXml, frame, colors, themeLineWidths = [], themeFillStyles = [], themeEffectStyles = []) {
173
+ const drawing = parseXml(diagramXml).find((n) => poIs(n, "dsp:drawing"));
174
+ const spTree = drawing ? poFirstChild(drawing, "dsp:spTree") : void 0;
175
+ if (!spTree) return [];
176
+ const out = [];
177
+ for (const sp of poChildren(spTree)) {
178
+ if (!poIs(sp, "dsp:sp")) continue;
179
+ const box = diagramBox(sp, frame);
36
180
  if (!box) continue;
37
- const spPr = poChildren(sp).find((c) => poIs(c, "xdr:spPr"));
38
- const geometry = parseGeometry(spPr);
39
- const fill = spPr ? parseFill(spPr, colors) : { kind: "none" };
40
- const line = spPr ? parseLine(spPr, colors) : void 0;
41
- const txBody = poChildren(sp).find((c) => poIs(c, "xdr:txBody"));
42
- const text = txBody ? parseTxBody(txBody, void 0, void 0, colors, void 0) : void 0;
43
- const visibleLine = line !== void 0 && line.fill !== "none";
44
- if (!text && fill.kind === "none" && !visibleLine) continue;
45
- shapes.push({
46
- shape: {
47
- width: pt(box.widthPt),
48
- height: pt(box.heightPt),
49
- geometry,
50
- fill,
51
- ...line ? { line } : {},
52
- ...text ? { text } : {},
53
- paragraphProperties: {}
181
+ const shape = buildShape(sp, box, "dsp", colors, themeLineWidths, themeFillStyles, themeEffectStyles);
182
+ if (!shape) continue;
183
+ const text = shape.text;
184
+ const label = text ? textBox(sp, frame) : void 0;
185
+ if (!label || !text) {
186
+ out.push(shape);
187
+ continue;
188
+ }
189
+ const { text: _moved, ...graphic } = shape;
190
+ out.push(graphic);
191
+ out.push({
192
+ float: {
193
+ wrap: "none",
194
+ posH: {
195
+ relativeFrom: "margin",
196
+ offsetPt: pt(label.xPt)
197
+ },
198
+ posV: {
199
+ relativeFrom: "margin",
200
+ offsetPt: pt(label.yPt)
201
+ }
202
+ },
203
+ width: pt(label.widthPt),
204
+ height: pt(label.heightPt),
205
+ geometry: {
206
+ kind: "preset",
207
+ preset: "rect",
208
+ adjust: /* @__PURE__ */ new Map()
54
209
  },
55
- anchorRow: box.anchorRow
210
+ fill: { kind: "none" },
211
+ text,
212
+ paragraphProperties: {}
56
213
  });
57
214
  }
58
- shapes.sort((a, b) => a.anchorRow - b.anchorRow);
59
- return shapes.map((s) => s.shape);
215
+ return out;
216
+ }
217
+ /** A diagram label's box: its `dsp:txXfrm`, moved to the frame's corner. */
218
+ function textBox(sp, frame) {
219
+ const xfrm = poFirstChild(sp, "dsp:txXfrm");
220
+ const ext = xfrm ? poFirstChild(xfrm, "a:ext") : void 0;
221
+ if (!ext) return void 0;
222
+ const widthPt = emuToPt(poIntAttr(ext, "cx") ?? 0);
223
+ const heightPt = emuToPt(poIntAttr(ext, "cy") ?? 0);
224
+ if (!(widthPt > 0 && heightPt > 0)) return void 0;
225
+ const off = poFirstChild(xfrm, "a:off");
226
+ return {
227
+ widthPt,
228
+ heightPt,
229
+ anchorRow: frame.anchorRow,
230
+ xPt: frame.xPt + emuToPt(poIntAttr(off, "x") ?? 0),
231
+ yPt: frame.yPt + emuToPt(poIntAttr(off, "y") ?? 0)
232
+ };
233
+ }
234
+ /** A diagram shape's box: its own `a:xfrm`, moved to the frame's corner. */
235
+ function diagramBox(sp, frame) {
236
+ const spPr = poFirstChild(sp, "dsp:spPr");
237
+ const xfrm = spPr ? poFirstChild(spPr, "a:xfrm") : void 0;
238
+ const off = xfrm ? poFirstChild(xfrm, "a:off") : void 0;
239
+ const ext = xfrm ? poFirstChild(xfrm, "a:ext") : void 0;
240
+ if (!ext) return void 0;
241
+ const widthPt = emuToPt(poIntAttr(ext, "cx") ?? 0);
242
+ const heightPt = emuToPt(poIntAttr(ext, "cy") ?? 0);
243
+ if (!(widthPt > 0 && heightPt > 0)) return void 0;
244
+ return {
245
+ widthPt,
246
+ heightPt,
247
+ anchorRow: frame.anchorRow,
248
+ xPt: frame.xPt + emuToPt(poIntAttr(off, "x") ?? 0),
249
+ yPt: frame.yPt + emuToPt(poIntAttr(off, "y") ?? 0)
250
+ };
251
+ }
252
+ /**
253
+ * Place a group's children inside the box the group itself occupies.
254
+ *
255
+ * §20.5.2.17 `xdr:grpSp` — a group gives its children a coordinate space of its
256
+ * own: `a:xfrm/a:chOff` + `a:chExt` name that space, and each child's own
257
+ * `a:xfrm` is a rectangle inside it. Mapping the rectangle onto the group's box
258
+ * places the child; a nested group recurses through the same map.
259
+ *
260
+ * @param group The `xdr:grpSp` element.
261
+ * @param box The box the group occupies on the page.
262
+ * @param out Collects each `xdr:sp` with the box it lands in.
263
+ */
264
+ function collectGrouped(group, box, out) {
265
+ const grpPr = poChildren(group).find((c) => poIs(c, "xdr:grpSpPr"));
266
+ const xfrm = grpPr ? poChildren(grpPr).find((c) => poIs(c, "a:xfrm")) : void 0;
267
+ const chOff = xfrm ? poChildren(xfrm).find((c) => poIs(c, "a:chOff")) : void 0;
268
+ const chExt = xfrm ? poChildren(xfrm).find((c) => poIs(c, "a:chExt")) : void 0;
269
+ const ox = chOff ? poIntAttr(chOff, "x") ?? 0 : 0;
270
+ const oy = chOff ? poIntAttr(chOff, "y") ?? 0 : 0;
271
+ const cw = chExt ? poIntAttr(chExt, "cx") ?? 0 : 0;
272
+ const ch = chExt ? poIntAttr(chExt, "cy") ?? 0 : 0;
273
+ if (!(cw > 0) || !(ch > 0)) return;
274
+ const place = (node, prTag) => {
275
+ const pr = poChildren(node).find((c) => poIs(c, prTag));
276
+ const x = pr ? poChildren(pr).find((c) => poIs(c, "a:xfrm")) : void 0;
277
+ const off = x ? poChildren(x).find((c) => poIs(c, "a:off")) : void 0;
278
+ const ext = x ? poChildren(x).find((c) => poIs(c, "a:ext")) : void 0;
279
+ if (!off || !ext) return void 0;
280
+ const w = (poIntAttr(ext, "cx") ?? 0) / cw * box.widthPt;
281
+ const h = (poIntAttr(ext, "cy") ?? 0) / ch * box.heightPt;
282
+ if (!(w > 0) || !(h > 0)) return void 0;
283
+ return {
284
+ widthPt: w,
285
+ heightPt: h,
286
+ anchorRow: box.anchorRow,
287
+ xPt: box.xPt + ((poIntAttr(off, "x") ?? 0) - ox) / cw * box.widthPt,
288
+ yPt: box.yPt + ((poIntAttr(off, "y") ?? 0) - oy) / ch * box.heightPt
289
+ };
290
+ };
291
+ for (const child of poChildren(group)) if (poIs(child, "xdr:sp")) {
292
+ const b = place(child, "xdr:spPr");
293
+ if (b) out.push({
294
+ sp: child,
295
+ box: b
296
+ });
297
+ } else if (poIs(child, "xdr:grpSp")) {
298
+ const b = place(child, "xdr:grpSpPr");
299
+ if (b) collectGrouped(child, b, out);
300
+ }
301
+ }
302
+ /**
303
+ * Whether a drawing says it is not to be shown.
304
+ *
305
+ * §20.1.2.2.8 `cNvPr@hidden` — "Specifies whether this DrawingML object shall
306
+ * be displayed", default false. POI writes one white rectangle per cell comment
307
+ * under the name `_xssf_cell_comment` and marks it hidden; read without the
308
+ * flag, 51850.xlsx grew a 494 × 677pt outlined box across both its pages.
309
+ *
310
+ * @param node The `xdr:sp` / `xdr:pic` / `xdr:graphicFrame` element.
311
+ * @param nvPropTag Its non-visual properties wrapper.
312
+ * @returns True when the object declares itself hidden.
313
+ */
314
+ function isHiddenDrawing(node, nvPropTag) {
315
+ const nv = poChildren(node).find((c) => poIs(c, nvPropTag));
316
+ const cNvPr = nv ? poChildren(nv).find((c) => poIs(c, "xdr:cNvPr")) : void 0;
317
+ const hidden = cNvPr ? poAttr(cNvPr, "hidden") : void 0;
318
+ return hidden === "1" || hidden === "true";
319
+ }
320
+ /**
321
+ * The outline `<xdr:style><a:lnRef>` names, if it names one.
322
+ *
323
+ * §20.1.4.2.19: the reference's `idx` is a 1-based index into the theme's
324
+ * `a:lnStyleLst`, which is where the WIDTH lives — the reference itself carries
325
+ * only the colour. Assuming a hairline drew 50299.xlsx's `idx="2"` rectangle in
326
+ * 0.75pt where its theme asks for 2pt.
327
+ */
328
+ function styleLine(sp, ns, colors, themeLineWidths) {
329
+ const style = poChildren(sp).find((c) => poIs(c, `${ns}:style`));
330
+ const lnRef = style ? poChildren(style).find((c) => poIs(c, "a:lnRef")) : void 0;
331
+ const child = poFirstElement(lnRef);
332
+ const colorHex = child ? resolveColorNode(child, colors) : void 0;
333
+ if (colorHex === void 0) return void 0;
334
+ const idx = Number(poAttr(lnRef, "idx") ?? "");
335
+ return {
336
+ colorHex,
337
+ width: pt((Number.isFinite(idx) ? themeLineWidths[idx - 1] : void 0) ?? .75)
338
+ };
339
+ }
340
+ /**
341
+ * The fill `<xdr:style><a:fillRef>` names, if it names one.
342
+ *
343
+ * §20.1.4.2.10, the same mechanism as the outline beside it: a shape drawn from
344
+ * a gallery style carries no `a:solidFill` in its `spPr` at all, and read alone
345
+ * it says the shape has no fill. 50299.xlsx's rectangle asks for `accent1` that
346
+ * way, and we drew it empty on all six sheets.
347
+ *
348
+ * `idx="0"` is the explicit "no fill" slot. Above it the reference is a 1-based
349
+ * index into the theme's `a:fillStyleLst` (§20.1.4.1.13), and THAT is the fill —
350
+ * the reference only says which colour goes where the style says `phClr`. The
351
+ * standard Office theme's third slot is a gradient, which is what 47504.xlsx
352
+ * asks for and what both references draw; painting the referenced colour flat
353
+ * instead lost the gradient on every gallery shape.
354
+ */
355
+ /**
356
+ * The first CHILD ELEMENT of a style reference — `<a:lnRef>`, `<a:fillRef>`,
357
+ * `<a:effectRef>`, `<a:fontRef>` — each of which wraps exactly one colour.
358
+ *
359
+ * Not `poChildren(ref)[0]`: a part saved with indentation puts a whitespace
360
+ * text node there, and the colour behind it went unread. Every gallery shape in
361
+ * tdf139763ShapeAnchor.xlsx lost its fill AND its outline that way and drew
362
+ * nothing at all on a page LibreOffice fills with two blue arrows.
363
+ *
364
+ * @param ref The style reference, or undefined.
365
+ * @returns Its first element child, or undefined.
366
+ */
367
+ function poFirstElement(ref) {
368
+ return ref ? poChildren(ref).find((c) => poTag(c) !== void 0) : void 0;
369
+ }
370
+ function styleFill(sp, ns, colors, themeFillStyles) {
371
+ const style = poChildren(sp).find((c) => poIs(c, `${ns}:style`));
372
+ const fillRef = style ? poChildren(style).find((c) => poIs(c, "a:fillRef")) : void 0;
373
+ if (!fillRef || poAttr(fillRef, "idx") === "0") return { kind: "none" };
374
+ const child = poFirstElement(fillRef);
375
+ const colorHex = child ? resolveColorNode(child, colors) : void 0;
376
+ if (colorHex === void 0) return { kind: "none" };
377
+ const idx = Number(poAttr(fillRef, "idx") ?? "");
378
+ const slot = Number.isFinite(idx) ? themeFillStyles[idx - 1] : void 0;
379
+ if (!slot) return {
380
+ kind: "solid",
381
+ colorHex
382
+ };
383
+ const themed = parseFill({ "a:spPr": [slot] }, placeholderColors(colors, colorHex));
384
+ return themed.kind === "none" ? {
385
+ kind: "solid",
386
+ colorHex
387
+ } : themed;
388
+ }
389
+ /**
390
+ * §20.1.4.2.10 — inside a theme's style list, `phClr` stands for "the colour the
391
+ * reference names". Every other colour resolves as it always did.
392
+ *
393
+ * @param colors The workbook's own resolver.
394
+ * @param phHex The colour the `a:fillRef` / `a:lnRef` carries.
395
+ * @returns A resolver that answers `phClr` with that colour, transforms and all.
396
+ */
397
+ function placeholderColors(colors, phHex) {
398
+ return (raw) => "scheme" in raw && raw.scheme === "phClr" ? applyColorMods(phHex, raw.mods ?? []) : colors(raw);
399
+ }
400
+ /**
401
+ * The shadow `<xdr:style><a:effectRef>` names, if it names one.
402
+ *
403
+ * §20.1.4.2.8, the same mechanism as the fill and the outline beside it: a
404
+ * shape drawn from a gallery style carries no `a:effectLst`, only an index into
405
+ * the theme's `a:effectStyleLst`, and the colour to put where the style says
406
+ * `phClr`. 47504.xlsx asks for slot 2 that way — the soft shadow both
407
+ * references draw under it, and which we drew not at all.
408
+ *
409
+ * @param sp The `xdr:sp` node.
410
+ * @param colors The workbook's colour resolver.
411
+ * @param themeEffectStyles The theme's `a:effectStyleLst` nodes.
412
+ * @returns The shadow, or undefined when the shape names no effect style.
413
+ */
414
+ function styleShadow(sp, ns, colors, themeEffectStyles) {
415
+ const style = poChildren(sp).find((c) => poIs(c, `${ns}:style`));
416
+ const ref = style ? poChildren(style).find((c) => poIs(c, "a:effectRef")) : void 0;
417
+ if (!ref) return void 0;
418
+ const idx = Number(poAttr(ref, "idx") ?? "");
419
+ if (!Number.isFinite(idx) || idx < 1) return void 0;
420
+ const slot = themeEffectStyles[idx - 1];
421
+ const list = slot ? poFirstChild(slot, "a:effectLst") : void 0;
422
+ const shdw = list ? poFirstChild(list, "a:outerShdw") : void 0;
423
+ if (!shdw) return void 0;
424
+ const child = poFirstElement(ref);
425
+ const phHex = child ? resolveColorNode(child, colors) : void 0;
426
+ return shadowFromOuterShdw(shdw, phHex ? placeholderColors(colors, phHex) : colors);
427
+ }
428
+ /** The colour `<xdr:style><a:fontRef>` names, if it names one. */
429
+ function styleFontColor(sp, ns, colors) {
430
+ const style = poChildren(sp).find((c) => poIs(c, `${ns}:style`));
431
+ const child = poFirstElement(style ? poChildren(style).find((c) => poIs(c, "a:fontRef")) : void 0);
432
+ return child ? resolveColorNode(child, colors) : void 0;
433
+ }
434
+ /** The text body with that colour filled in wherever a run declares none. */
435
+ function withStyleTextColor(text, sp, ns, colors) {
436
+ const colorHex = styleFontColor(sp, ns, colors);
437
+ if (colorHex === void 0) return text;
438
+ return {
439
+ ...text,
440
+ content: text.content.map((block) => block.kind === "paragraph" ? {
441
+ ...block,
442
+ paragraph: {
443
+ ...block.paragraph,
444
+ runs: block.paragraph.runs.map((run) => run.properties.colorHex === void 0 ? {
445
+ ...run,
446
+ properties: {
447
+ ...run.properties,
448
+ colorHex
449
+ }
450
+ } : run)
451
+ }
452
+ } : block)
453
+ };
60
454
  }
61
455
  function anchorBox(anchor, colWidthPt, rowHeightPt) {
62
456
  const from = marker(poChildren(anchor).find((c) => poIs(c, "xdr:from")));
@@ -65,21 +459,56 @@ function anchorBox(anchor, colWidthPt, rowHeightPt) {
65
459
  if (!from || !to) return void 0;
66
460
  const widthPt = span(from.col, from.colOffPt, to.col, to.colOffPt, colWidthPt);
67
461
  const heightPt = span(from.row, from.rowOffPt, to.row, to.rowOffPt, rowHeightPt);
68
- return widthPt > 0 && heightPt > 0 ? {
462
+ if (!(widthPt > 0 && heightPt > 0)) return void 0;
463
+ return {
69
464
  widthPt,
70
465
  heightPt,
71
- anchorRow: from.row
72
- } : void 0;
466
+ anchorRow: from.row,
467
+ xPt: origin(from.col, from.colOffPt, colWidthPt),
468
+ yPt: origin(from.row, from.rowOffPt, rowHeightPt)
469
+ };
73
470
  }
74
471
  const ext = poChildren(anchor).find((c) => poIs(c, "xdr:ext"));
75
472
  if (!ext) return void 0;
473
+ const pos = absolutePos(anchor);
76
474
  const widthPt = emuToPt(poIntAttr(ext, "cx") ?? 0);
77
475
  const heightPt = emuToPt(poIntAttr(ext, "cy") ?? 0);
78
- return widthPt > 0 && heightPt > 0 ? {
476
+ if (!(widthPt > 0 && heightPt > 0)) return void 0;
477
+ return {
79
478
  widthPt,
80
479
  heightPt,
81
- anchorRow: from?.row ?? 0
82
- } : void 0;
480
+ anchorRow: from?.row ?? 0,
481
+ xPt: from ? origin(from.col, from.colOffPt, colWidthPt) : pos.xPt,
482
+ yPt: from ? origin(from.row, from.rowOffPt, rowHeightPt) : pos.yPt
483
+ };
484
+ }
485
+ /**
486
+ * §20.5.2.1 `absoluteAnchor` names no cell: it gives the distance from the
487
+ * sheet's own corner in `<xdr:pos>`. Read nowhere, both of
488
+ * tdf139763ShapeAnchor.xlsx's arrows piled into the top-left corner.
489
+ *
490
+ * @param anchor The anchor element.
491
+ * @returns The offset in points; zero for an anchor that states none.
492
+ */
493
+ function absolutePos(anchor) {
494
+ const node = poChildren(anchor).find((c) => poIs(c, "xdr:pos"));
495
+ if (!node) return {
496
+ xPt: 0,
497
+ yPt: 0
498
+ };
499
+ return {
500
+ xPt: emuToPt(poIntAttr(node, "x") ?? 0),
501
+ yPt: emuToPt(poIntAttr(node, "y") ?? 0)
502
+ };
503
+ }
504
+ /**
505
+ * The distance from the sheet's origin to a marker: every whole track before it
506
+ * plus the marker's own offset into the one it lands in.
507
+ */
508
+ function origin(index, offsetPt, trackPt) {
509
+ let total = 0;
510
+ for (let i = 0; i < index; i++) total += trackPt(i);
511
+ return total + offsetPt;
83
512
  }
84
513
  function marker(node) {
85
514
  if (!node) return void 0;
@@ -105,4 +534,4 @@ function span(from, fromOffPt, to, toOffPt, trackPt) {
105
534
  return total - fromOffPt + toOffPt;
106
535
  }
107
536
  //#endregion
108
- export { parseSheetShapes };
537
+ export { parseDiagramFrames, parseDiagramShapes, parseSheetShapes };
@@ -21,12 +21,33 @@ export interface ProjectSheetOptions {
21
21
  * unreported.
22
22
  */
23
23
  readonly losses?: Array<Loss>;
24
+ /**
25
+ * Width of a digit in the font the document will actually be rendered in, at
26
+ * the workbook's default size, in points.
27
+ *
28
+ * §18.3.1.13 measures a column in Maximum Digit Widths of the workbook's own
29
+ * default font — the unit is a property of the font, not a constant. Laying
30
+ * columns out in one font's digit and then drawing the text in another makes
31
+ * every column the wrong width for its contents: with Calibri's 5.25 pt
32
+ * assumed and Roboto's 6.18 pt drawn, a column holds a sixth less than the
33
+ * file says, and the text that does not fit is clipped away.
34
+ *
35
+ * Omitted ⇒ Excel's own 7 px, which is right only if the render font matches
36
+ * the workbook's.
37
+ */
38
+ readonly digitWidthPt?: number;
39
+ /**
40
+ * §18.3.1.34 `&F` — the workbook's file name, for a header or footer that
41
+ * prints it. The reader takes bytes and cannot know it, so the caller
42
+ * supplies it; absent, the code is dropped exactly as before.
43
+ */
44
+ readonly fileName?: string;
24
45
  }
25
46
  /**
26
47
  * Project a {@link SheetDoc} into a {@link FlowDoc} (E-SHEET SA2): each grid sheet
27
48
  * becomes flow blocks (a table + chart/picture/shape/slicer frames, then comment
28
- * / control listings), with sheets after the first starting on a new page. The
29
- * first sheet's page geometry + header/footer drive the document section.
49
+ * / control listings), with sheets after the first starting on a new page. Each
50
+ * sheet gets its own section — its page geometry and its own header/footer.
30
51
  *
31
52
  * @param sheet The SpreadsheetML IR tree.
32
53
  * @param options Projection knobs (the W9 reference date).