reamkit 1.22.0 → 1.24.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 (101) hide show
  1. package/dist/esm/core/bmp.d.ts +33 -0
  2. package/dist/esm/core/bmp.js +276 -0
  3. package/dist/esm/core/converter/ream.d.ts +16 -0
  4. package/dist/esm/core/converter/ream.js +59 -9
  5. package/dist/esm/core/document-model/types.d.ts +55 -0
  6. package/dist/esm/core/drawingml/chart-geometry.js +14 -7
  7. package/dist/esm/core/drawingml/colors.d.ts +4 -2
  8. package/dist/esm/core/drawingml/colors.js +15 -0
  9. package/dist/esm/core/drawingml/diagram/colors.d.ts +52 -0
  10. package/dist/esm/core/drawingml/diagram/colors.js +125 -0
  11. package/dist/esm/core/drawingml/diagram/data-model.d.ts +47 -0
  12. package/dist/esm/core/drawingml/diagram/data-model.js +119 -0
  13. package/dist/esm/core/drawingml/diagram/layout-engine.d.ts +46 -0
  14. package/dist/esm/core/drawingml/diagram/layout-engine.js +948 -0
  15. package/dist/esm/core/drawingml/diagram/run.d.ts +37 -0
  16. package/dist/esm/core/drawingml/diagram/run.js +50 -0
  17. package/dist/esm/core/drawingml/diagram/to-drawing.d.ts +16 -0
  18. package/dist/esm/core/drawingml/diagram/to-drawing.js +86 -0
  19. package/dist/esm/core/drawingml/preset-geometry.js +116 -6
  20. package/dist/esm/core/drawingml/text-warp.d.ts +58 -0
  21. package/dist/esm/core/drawingml/text-warp.js +355 -0
  22. package/dist/esm/core/drawingml/theme-parser.d.ts +38 -0
  23. package/dist/esm/core/drawingml/theme-parser.js +73 -4
  24. package/dist/esm/core/font/measure.d.ts +12 -0
  25. package/dist/esm/core/font/measure.js +36 -0
  26. package/dist/esm/core/fonts/families.d.ts +10 -0
  27. package/dist/esm/core/fonts/families.js +44 -0
  28. package/dist/esm/core/fonts/index.d.ts +3 -2
  29. package/dist/esm/core/fonts/remote-fonts.d.ts +50 -0
  30. package/dist/esm/core/fonts/remote-fonts.js +114 -8
  31. package/dist/esm/core/fonts/scripts.d.ts +26 -0
  32. package/dist/esm/core/fonts/scripts.js +177 -0
  33. package/dist/esm/core/images.d.ts +30 -2
  34. package/dist/esm/core/images.js +121 -5
  35. package/dist/esm/core/metafile/blit.d.ts +33 -0
  36. package/dist/esm/core/metafile/blit.js +67 -0
  37. package/dist/esm/core/metafile/dib.d.ts +61 -0
  38. package/dist/esm/core/metafile/dib.js +205 -0
  39. package/dist/esm/core/metafile/emf.js +280 -38
  40. package/dist/esm/core/metafile/picture.d.ts +61 -1
  41. package/dist/esm/core/metafile/picture.js +70 -1
  42. package/dist/esm/core/metafile/wmf.js +202 -13
  43. package/dist/esm/core/ole/escher-blip.d.ts +12 -0
  44. package/dist/esm/core/ole/escher-blip.js +98 -0
  45. package/dist/esm/{pdf-reader → core}/png-encode.js +1 -1
  46. package/dist/esm/excel/print-model.js +5 -3
  47. package/dist/esm/excel/sheet-drawing.js +1 -1
  48. package/dist/esm/excel/sheet-to-flow.js +16 -2
  49. package/dist/esm/excel/styles-parser.js +1 -1
  50. package/dist/esm/excel/xls/biff-reader.js +53 -18
  51. package/dist/esm/excel/xls/biff-styles.d.ts +5 -2
  52. package/dist/esm/excel/xls/biff-styles.js +9 -7
  53. package/dist/esm/excel/xls/escher.js +90 -24
  54. package/dist/esm/html/html-writer.js +2 -1
  55. package/dist/esm/layout/page-doc.d.ts +74 -0
  56. package/dist/esm/layout/page-doc.js +14 -1
  57. package/dist/esm/layout/styled-layout.d.ts +2 -2
  58. package/dist/esm/layout/styled-layout.js +475 -48
  59. package/dist/esm/pdf/shading.js +8 -6
  60. package/dist/esm/pdf/styled-page-emitter.js +77 -5
  61. package/dist/esm/pdf/vector-graphics.js +1 -1
  62. package/dist/esm/pdf-reader/image-decode.js +1 -1
  63. package/dist/esm/pptx/embedded-fonts.d.ts +31 -0
  64. package/dist/esm/pptx/embedded-fonts.js +104 -0
  65. package/dist/esm/pptx/placeholder-cascade.d.ts +73 -6
  66. package/dist/esm/pptx/placeholder-cascade.js +114 -18
  67. package/dist/esm/pptx/ppt/ppt-reader.js +191 -13
  68. package/dist/esm/pptx/ppt/ppt-text.d.ts +107 -1
  69. package/dist/esm/pptx/ppt/ppt-text.js +873 -144
  70. package/dist/esm/pptx/pptx-reader.d.ts +8 -0
  71. package/dist/esm/pptx/pptx-reader.js +117 -15
  72. package/dist/esm/pptx/preset-table-styles.d.ts +9 -0
  73. package/dist/esm/pptx/preset-table-styles.js +27 -0
  74. package/dist/esm/pptx/slide-parser.d.ts +11 -2
  75. package/dist/esm/pptx/slide-parser.js +203 -44
  76. package/dist/esm/pptx/sp-helpers.d.ts +9 -4
  77. package/dist/esm/pptx/sp-helpers.js +48 -9
  78. package/dist/esm/pptx/table-style.d.ts +16 -1
  79. package/dist/esm/pptx/table-style.js +104 -10
  80. package/dist/esm/svg/svg-writer.js +1 -0
  81. package/dist/esm/word/document-parser.d.ts +11 -2
  82. package/dist/esm/word/document-parser.js +10 -7
  83. package/dist/esm/word/docx-reader.js +32 -16
  84. package/dist/esm/word/docx-to-pdf.d.ts +8 -7
  85. package/dist/esm/word/docx-to-pdf.js +25 -7
  86. package/dist/esm/word/docx-writer.js +4 -0
  87. package/dist/esm/word/drawing-parser.d.ts +20 -0
  88. package/dist/esm/word/drawing-parser.js +67 -22
  89. package/dist/esm/word/numbering-parser.d.ts +2 -1
  90. package/dist/esm/word/numbering-parser.js +6 -6
  91. package/dist/esm/word/paragraph-properties.d.ts +2 -1
  92. package/dist/esm/word/paragraph-properties.js +2 -2
  93. package/dist/esm/word/run-properties.d.ts +5 -2
  94. package/dist/esm/word/run-properties.js +13 -8
  95. package/dist/esm/word/styles-parser.d.ts +2 -1
  96. package/dist/esm/word/styles-parser.js +12 -12
  97. package/dist/esm/word/table-parser.js +6 -3
  98. package/dist/esm/word/theme-fonts.d.ts +10 -0
  99. package/dist/esm/word/theme-fonts.js +28 -0
  100. package/package.json +1 -1
  101. /package/dist/esm/{pdf-reader → core}/png-encode.d.ts +0 -0
@@ -0,0 +1,948 @@
1
+ import { poAttr, poChildren, poIs, poTag } from "../../po-helpers.js";
2
+ //#region src/core/drawingml/diagram/layout-engine.ts
3
+ /**
4
+ * Lay a diagram out from its data and layout parts.
5
+ *
6
+ * @param layout The parsed `layout1.xml` tree.
7
+ * @param data The parsed data part.
8
+ * @param cx The frame's width in EMU, as the drawing states it.
9
+ * @param cy The frame's height in EMU.
10
+ * @returns The boxes to draw, or an empty list when the layout uses an
11
+ * algorithm this slice does not run — the caller then reports the
12
+ * diagram as a loss exactly as before.
13
+ */
14
+ function layoutDiagram(layout, data, cx, cy) {
15
+ const root = findChild(layout, "dgm:layoutDef");
16
+ const top = root ? findChild(poChildren(root), "dgm:layoutNode") : void 0;
17
+ if (!top || !data.root) return [];
18
+ const parsed = parseNode(top, {
19
+ maxDepth: data.depth,
20
+ data,
21
+ root: data.root
22
+ });
23
+ if (parsed.alg !== "snake" && parsed.alg !== "lin" && parsed.alg !== "composite" && parsed.alg !== "cycle" && parsed.alg !== "hier") return [];
24
+ return layoutIn(parsed, data.root, {
25
+ x: 0,
26
+ y: 0,
27
+ cx,
28
+ cy
29
+ }, data, 0);
30
+ }
31
+ /**
32
+ * One layout node run inside the box it was given, by whichever algorithm it
33
+ * names.
34
+ *
35
+ * @param node The layout node.
36
+ * @param point The data point it is laying out.
37
+ * @param box The rectangle it has to fill.
38
+ * @param data The data part.
39
+ * @param index The point's place among its siblings, for the colour run.
40
+ */
41
+ function layoutIn(node, point, box, data, index) {
42
+ if (node.alg === "composite") return compositeLayout(node, point, box, data, index);
43
+ if (node.alg === "hier") return hierarchyLayout(node, point, box, data);
44
+ if (node.alg === "cycle") return cycleLayout(node, point, box, data);
45
+ if (node.alg === "lin" || node.alg === "snake") return listLayout(node, point, box, data);
46
+ if (node.alg === "sp") {
47
+ const own = spokenFor(node, point, data).spPr;
48
+ return node.shapeType === void 0 ? [] : [{
49
+ point: {
50
+ id: `${point.id}/${node.name}`,
51
+ type: "node",
52
+ ...own !== void 0 ? { spPr: own } : {}
53
+ },
54
+ shapeType: node.shapeType,
55
+ ...styleOf(node),
56
+ index,
57
+ ...box
58
+ }];
59
+ }
60
+ if (node.alg !== "tx") return [];
61
+ return [{
62
+ point: spokenFor(node, point, data),
63
+ shapeType: node.shapeType ?? "rect",
64
+ ...styleOf(node),
65
+ index,
66
+ ...box
67
+ }];
68
+ }
69
+ /**
70
+ * §21.4.3.2 `hierRoot`/`hierChild` — the hierarchy family, which is the org
71
+ * chart. `hierRoot` puts a node's box above its subtree and `hierChild` ranges
72
+ * that subtree beneath it, each child being a `hierRoot` again; the two are one
73
+ * tree walk written as two algorithms, and the engine runs them as one.
74
+ *
75
+ * The layout program that expresses this is deeply nested — a `choose` per
76
+ * branch style at every level, with the same three boxes renamed each time —
77
+ * but it says only one thing, and it says it in the constraints at the top: a
78
+ * box is `w` wide and `0.5w` tall, levels are `0.21w` apart and so are
79
+ * siblings. So the geometry is read from there and the shape of the tree from
80
+ * the DATA, which is where it actually lives.
81
+ *
82
+ * @param node The `hierChild`/`hierRoot` node at the top of the hierarchy.
83
+ * @param point The data point it is rooted at.
84
+ * @param box The frame the tree has to fit.
85
+ * @param data The data part.
86
+ */
87
+ function hierarchyLayout(node, point, box, data) {
88
+ const f = hierFactors(node.constrs);
89
+ const face = firstTextBox(node);
90
+ const build = (p) => {
91
+ const kids = data.children(p.id, "node").map(build);
92
+ const spread = kids.length === 0 ? 0 : kids.reduce((a, k) => a + k.units, 0) + f.sibSp * (kids.length - 1);
93
+ return {
94
+ point: p,
95
+ kids,
96
+ units: Math.max(1, spread),
97
+ spread
98
+ };
99
+ };
100
+ const roots = data.children(point.id, "node").map(build);
101
+ if (roots.length === 0) return [];
102
+ const wide = roots.reduce((a, r) => a + r.units, 0) + f.sibSp * (roots.length - 1);
103
+ const rows = (t) => 1 + t.kids.reduce((a, k) => Math.max(a, rows(k)), 0);
104
+ const deep = roots.reduce((a, r) => Math.max(a, rows(r)), 0);
105
+ const tall = deep * f.aspect + (deep - 1) * f.sp;
106
+ if (wide <= 0 || tall <= 0) return [];
107
+ const w = Math.min(box.cx / wide, box.cy / tall);
108
+ const out = [];
109
+ const top = box.y + (box.cy - tall * w) / 2;
110
+ const place = (t, left, depth) => {
111
+ out.push({
112
+ point: t.point,
113
+ shapeType: face?.shapeType ?? "rect",
114
+ styleLbl: `node${Math.min(depth, 4)}`,
115
+ ...face === void 0 ? {} : textPlacing(face),
116
+ index: depth,
117
+ x: left + (t.units * w - w) / 2,
118
+ y: top + depth * (f.aspect + f.sp) * w,
119
+ cx: w,
120
+ cy: f.aspect * w
121
+ });
122
+ let at = left + (t.units - t.spread) * w / 2;
123
+ for (const kid of t.kids) {
124
+ place(kid, at, depth + 1);
125
+ at += (kid.units + f.sibSp) * w;
126
+ }
127
+ };
128
+ let at = box.x + (box.cx - wide * w) / 2;
129
+ for (const root of roots) {
130
+ place(root, at, 0);
131
+ at += (root.units + f.sibSp) * w;
132
+ }
133
+ return out;
134
+ }
135
+ function hierFactors(constrs) {
136
+ let aspect = .5;
137
+ let sp = .21;
138
+ let sibSp = .21;
139
+ for (const k of constrs) {
140
+ if (k.refType !== "w") continue;
141
+ if (k.type === "h" && k.refForName !== void 0) aspect = k.fact;
142
+ if (k.type === "sp") sp = k.fact;
143
+ if (k.type === "sibSp") sibSp = k.fact;
144
+ }
145
+ return {
146
+ aspect,
147
+ sp,
148
+ sibSp
149
+ };
150
+ }
151
+ function firstTextBox(node) {
152
+ const walk = (items) => {
153
+ for (const item of items) {
154
+ if (item.kind === "node" && item.alg === "tx") return item;
155
+ const found = walk(item.children);
156
+ if (found) return found;
157
+ }
158
+ };
159
+ return walk(node.children);
160
+ }
161
+ function textPlacing(node) {
162
+ const anchor = node.algParams.get("txAnchorVert");
163
+ const align = node.algParams.get("parTxLTRAlign");
164
+ return {
165
+ ...anchor !== void 0 && anchor !== "" ? { anchor } : {},
166
+ ...align !== void 0 && align !== "" ? { align } : {}
167
+ };
168
+ }
169
+ function styleOf(node) {
170
+ const sz = fontSize(node.constrs, void 0, "primFontSz");
171
+ const anchor = node.algParams.get("txAnchorVert");
172
+ const align = node.algParams.get("parTxLTRAlign");
173
+ return {
174
+ ...node.styleLbl !== void 0 ? { styleLbl: node.styleLbl } : {},
175
+ ...opt("fontSizePt", sz),
176
+ ...bulleted(node) ? { bulleted: true } : {},
177
+ ...node.hideGeom === true ? { hideGeom: true } : {},
178
+ ...anchor !== void 0 && anchor !== "" ? { anchor } : {},
179
+ ...align !== void 0 && align !== "" ? { align } : {},
180
+ ...node.shapeRotation !== void 0 ? { rotation60k: node.shapeRotation * 6e4 } : {}
181
+ };
182
+ }
183
+ /**
184
+ * §21.4.3.7 `dgm:presOf` — which points a box speaks for. `desOrSelf` is the
185
+ * point and everything under it; `des` is everything under it and NOT the point,
186
+ * which is how a list's second box holds the children's words and not a second
187
+ * copy of the label. Anything else is the point itself.
188
+ */
189
+ function spokenFor(box, point, data) {
190
+ if (box.presOfPath) return walkPath(box.presOfPath, point, data);
191
+ if (box.presOfAxis === "desOrSelf") return withDescendants(point, data);
192
+ if (box.presOfAxis === "des") return gatheredText(point, data.children(point.id, "node"));
193
+ return point;
194
+ }
195
+ /**
196
+ * §21.4.3.7 — the `st`/`cnt` slice of a `presOf` path, read from the point the
197
+ * box is being laid out for.
198
+ *
199
+ * The cycle-matrix layout is the reason this exists: each of its four quadrants
200
+ * names ONE child (`axis="ch" st="3" cnt="1"`) and each of the four callouts
201
+ * beside them names that child's own children (`axis="ch des" st="3 1"
202
+ * cnt="1 0"`). Read as a plain axis, all eight boxes spoke for the root and the
203
+ * whole diagram came out blank.
204
+ *
205
+ * @param path The parsed axis / start / count triple.
206
+ * @param point The point the box is laid out for.
207
+ * @param data The data part, for walking children.
208
+ * @returns One point carrying the text of everything the path reached.
209
+ */
210
+ function walkPath(path, point, data) {
211
+ let at = [point];
212
+ path.axis.forEach((step, i) => {
213
+ const next = step === "ch" ? at.flatMap((p) => data.children(p.id, "node")) : step === "des" ? at.flatMap((p) => descendants(p, data)) : step === "self" ? at : [];
214
+ const from = Math.max(0, (path.st[i] ?? 1) - 1);
215
+ const want = path.cnt[i] ?? 0;
216
+ at = next.slice(from, want === 0 ? next.length : from + want);
217
+ });
218
+ if (at.length === 1) return at[0];
219
+ const paragraphs = at.flatMap((p) => p.text ? poChildren(p.text) : []);
220
+ const id = `${point.id}/${path.axis.join(".")}`;
221
+ if (paragraphs.length === 0) return {
222
+ id,
223
+ type: "node"
224
+ };
225
+ return {
226
+ id,
227
+ type: "node",
228
+ text: { "dgm:t": paragraphs }
229
+ };
230
+ }
231
+ /** Every point under `point`, depth first — the `des` axis. */
232
+ function descendants(point, data) {
233
+ const out = [];
234
+ const walk = (id) => {
235
+ for (const kid of data.children(id, "node")) {
236
+ out.push(kid);
237
+ walk(kid.id);
238
+ }
239
+ };
240
+ walk(point.id);
241
+ return out;
242
+ }
243
+ /**
244
+ * §21.4.3.7 — a `presOf`'s axis PATH, when it names more than one step or
245
+ * slices the one it names. A single unsliced axis stays with the plain
246
+ * `presOfAxis` reading, which the rest of the engine already answers.
247
+ *
248
+ * @param presOf The `dgm:presOf` element, when the box has one.
249
+ * @returns The path, or `undefined` when there is nothing to walk.
250
+ */
251
+ function axisPath(presOf) {
252
+ if (!presOf) return void 0;
253
+ const axis = (poAttr(presOf, "axis") ?? "").split(/\s+/u).filter((a) => a !== "");
254
+ const nums = (name) => (poAttr(presOf, name) ?? "").split(/\s+/u).filter((v) => v !== "").map((v) => Number(v));
255
+ const st = nums("st");
256
+ const cnt = nums("cnt");
257
+ if (axis.length === 0) return void 0;
258
+ if (axis.length === 1 && st.length === 0 && cnt.length === 0) return void 0;
259
+ if (!axis.every((a) => a === "ch" || a === "des" || a === "self")) return void 0;
260
+ return {
261
+ axis,
262
+ st,
263
+ cnt
264
+ };
265
+ }
266
+ function withDescendants(point, data) {
267
+ const paragraphs = point.text ? [...poChildren(point.text)] : [];
268
+ const walk = (id) => {
269
+ for (const kid of data.children(id, "node")) {
270
+ if (kid.text) paragraphs.push(...poChildren(kid.text));
271
+ walk(kid.id);
272
+ }
273
+ };
274
+ walk(point.id);
275
+ if (paragraphs.length === 0) return point;
276
+ return {
277
+ ...point,
278
+ text: { "dgm:t": paragraphs }
279
+ };
280
+ }
281
+ /**
282
+ * §21.4.3.2 `composite` — the children are not a list, they are placed: each
283
+ * states where its edges are against the parent's box or against a sibling's,
284
+ * and then runs its own algorithm inside what it was given. A process arrow
285
+ * across the frame with the steps standing along it is one node laid over
286
+ * another, which no list algorithm can express.
287
+ */
288
+ /**
289
+ * §21.4.3.4 `<dgm:param type="ar"/>` — the width-to-height ratio the algorithm
290
+ * wants its cell in. The cell shrinks to it on whichever axis is over, and
291
+ * stays centred on what it had.
292
+ *
293
+ * The cycle-matrix layout is the reason this matters: it asks for 1.3, measures
294
+ * its disc against the WIDTH it gets, and on a wide slide placeholder the disc
295
+ * came out half again too big and its four quarters overlapped in a pinwheel.
296
+ *
297
+ * @param node The layout node, for its algorithm parameters.
298
+ * @param box The cell it was given.
299
+ * @returns The cell at the stated ratio, or unchanged when it states none.
300
+ */
301
+ function aspectFitted(node, box) {
302
+ const ar = Number(node.algParams.get("ar") ?? "");
303
+ if (!Number.isFinite(ar) || ar <= 0 || box.cx <= 0 || box.cy <= 0) return box;
304
+ const cx = Math.min(box.cx, box.cy * ar);
305
+ const cy = Math.min(box.cy, box.cx / ar);
306
+ return {
307
+ x: box.x + (box.cx - cx) / 2,
308
+ y: box.y + (box.cy - cy) / 2,
309
+ cx,
310
+ cy
311
+ };
312
+ }
313
+ function compositeLayout(node, point, box, data, index, outer = []) {
314
+ const kids = namedBoxes(node);
315
+ box = aspectFitted(node, box);
316
+ const run = (fontEmu) => {
317
+ const placed = /* @__PURE__ */ new Map();
318
+ return kids.map((child) => {
319
+ const rect = rectIn(node.constrs, child.name, box, placed, fontEmu, outer);
320
+ placed.set(child.name, rect);
321
+ return rect;
322
+ });
323
+ };
324
+ let rects = run(0);
325
+ if (kids.every((c) => fontHeight(outer, c.name) !== void 0)) {
326
+ const units = run(1).reduce((a, r) => Math.max(a, r.y - box.y + r.cy), 0);
327
+ if (units > 0) rects = run(box.cy / units);
328
+ }
329
+ const out = [];
330
+ kids.forEach((child, i) => {
331
+ const rect = rects[i];
332
+ if (rect) out.push(...layoutIn(child, point, rect, data, index));
333
+ });
334
+ return out;
335
+ }
336
+ function fontHeight(constrs, name) {
337
+ for (const k of constrs) if (k.type === "h" && k.forName === name && k.refType === "primFontSz" && k.op === void 0) return k.fact;
338
+ }
339
+ function places(constrs) {
340
+ return constrs.some((k) => k.type === "ctrX" || k.type === "ctrY" || k.type === "r" || k.type === "b" || (k.type === "l" || k.type === "t") && k.refType !== void 0 && k.refForName === void 0);
341
+ }
342
+ function rectIn(constrs, name, box, placed, fontEmu = 0, outer = []) {
343
+ let cx = box.cx;
344
+ let cy = box.cy;
345
+ let x;
346
+ let y;
347
+ const ref = (k) => {
348
+ const from = k.refForName === void 0 ? box : k.refForName === name ? {
349
+ cx,
350
+ cy
351
+ } : placed.get(k.refForName) ?? box;
352
+ return (k.refType === "w" ? from.cx : k.refType === "h" ? from.cy : k.refType === "primFontSz" ? fontEmu : 0) * k.fact;
353
+ };
354
+ const mine = [...outer, ...constrs].filter((k) => k.forName === name);
355
+ for (const k of mine) {
356
+ const v = ref(k);
357
+ switch (k.type) {
358
+ case "w":
359
+ cx = k.op === "lte" ? Math.min(cx, v) : v;
360
+ break;
361
+ case "h":
362
+ cy = k.op === "lte" ? Math.min(cy, v) : v;
363
+ break;
364
+ case "l":
365
+ x = box.x + v;
366
+ break;
367
+ case "r":
368
+ x = box.x + v - cx;
369
+ break;
370
+ case "t":
371
+ y = box.y + v;
372
+ break;
373
+ case "b":
374
+ y = box.y + v - cy;
375
+ break;
376
+ case "ctrX":
377
+ x = box.x + v - cx / 2;
378
+ break;
379
+ case "ctrY":
380
+ y = box.y + v - cy / 2;
381
+ break;
382
+ default: break;
383
+ }
384
+ }
385
+ return {
386
+ x: x ?? box.x,
387
+ y: y ?? box.y,
388
+ cx,
389
+ cy
390
+ };
391
+ }
392
+ /**
393
+ * §21.4.3.2 `cycle` — the point's children around a circle, starting at
394
+ * `stAng` and spanning `spanAng` (both in degrees, clockwise from twelve
395
+ * o'clock). `ctrShpMap="fNode"` makes the FIRST child the hub in the middle
396
+ * and its own children the ring around it, which is what a radial cycle is.
397
+ */
398
+ function cycleLayout(node, point, box, data) {
399
+ const boxes = namedBoxes(node);
400
+ const hubNode = node.algParams.get("ctrShpMap") === "fNode" ? boxes[0] : void 0;
401
+ const ringNode = boxes[boxes.length - 1];
402
+ const kids = data.children(point.id, "node");
403
+ const hub = hubNode ? kids[0] : void 0;
404
+ const ring = hub ? data.children(hub.id, "node") : kids;
405
+ if (ring.length === 0 || !ringNode) return [];
406
+ const n = ring.length;
407
+ const stAng = Number(node.algParams.get("stAng") ?? "0");
408
+ const spanAng = Number(node.algParams.get("spanAng") ?? "360");
409
+ const step = spanAng / (Math.abs(spanAng) >= 359.5 ? n : Math.max(1, n - 1));
410
+ const stated = sizeFact(node.constrs, ringNode.name, "w");
411
+ const gap = siblingSpace(node.constrs, ringNode.name);
412
+ const chord = Math.sin(Math.PI / Math.max(2, n));
413
+ const least = Math.min(box.cx, box.cy);
414
+ const size = stated !== void 0 && stated < .99 ? {
415
+ cx: box.cx * stated,
416
+ cy: box.cy * (sizeFact(node.constrs, ringNode.name, "h") ?? stated)
417
+ } : {
418
+ cx: least * chord / (1 + gap + chord),
419
+ cy: least * chord / (1 + gap + chord)
420
+ };
421
+ const radius = Math.min((box.cx - size.cx) / 2, (box.cy - size.cy) / 2);
422
+ const midX = box.x + box.cx / 2;
423
+ const midY = box.y + box.cy / 2;
424
+ const out = [];
425
+ if (hub && hubNode) out.push(...splitCell(node, hubNode.name, hub, data, {
426
+ x: midX - size.cx / 2,
427
+ y: midY - size.cy / 2,
428
+ cx: size.cx,
429
+ cy: size.cy
430
+ }, hubNode.shapeType ?? "rect", 0));
431
+ ring.forEach((pt, i) => {
432
+ const a = (stAng + i * step) * Math.PI / 180;
433
+ const cellX = midX + radius * Math.sin(a) - size.cx / 2;
434
+ const cellY = midY - radius * Math.cos(a) - size.cy / 2;
435
+ out.push(...splitCell(node, ringNode.name, pt, data, {
436
+ x: cellX,
437
+ y: cellY,
438
+ cx: size.cx,
439
+ cy: size.cy
440
+ }, ringNode.shapeType ?? "rect", i));
441
+ });
442
+ return out;
443
+ }
444
+ function siblingSpace(constrs, childName) {
445
+ for (const k of constrs) if (k.type === "sibSp" && (k.refForName === childName || k.refForName === void 0)) return k.fact;
446
+ return .5;
447
+ }
448
+ /**
449
+ * §21.4.3.2 `lin` / `snake` — the point's children in a row or a column, with
450
+ * the gap the constraints state between them. `snake` wraps; `lin` does not.
451
+ */
452
+ function listLayout(parsed, parent, box, data) {
453
+ const nodes = data.children(parent.id, "node");
454
+ if (nodes.length === 0) return [];
455
+ const childName = firstNodeName(parsed.children) ?? "node";
456
+ const c = resolve(parsed.constrs, childName);
457
+ const shapeType = childShapeType(parsed.children) ?? "rect";
458
+ const vertical = isVertical(parsed);
459
+ const body = namedBoxes(parsed);
460
+ const trans = transitionBox(parsed);
461
+ const n = nodes.length;
462
+ const cols = vertical ? 1 : parsed.alg === "snake" ? bestColumns(n, box.cx, box.cy, c.aspect, c.gap) : n;
463
+ const rows = Math.ceil(n / cols);
464
+ const cellW = box.cx / (cols + Math.max(0, cols - 1) * c.gap);
465
+ const gapW = cellW * c.gap;
466
+ const cellH = box.cy / (rows + Math.max(0, rows - 1) * c.gap);
467
+ const gapH = cellH * c.gap;
468
+ const out = [];
469
+ nodes.forEach((point, i) => {
470
+ const col = vertical ? Math.floor(i / rows) : i % cols;
471
+ const row = vertical ? i % rows : Math.floor(i / cols);
472
+ const inRow = vertical ? rows : Math.min(cols, n - row * cols);
473
+ const rowWidth = inRow * cellW + Math.max(0, inRow - 1) * gapW;
474
+ const left = vertical ? 0 : (box.cx - rowWidth) / 2;
475
+ const cell = {
476
+ x: box.x + left + col * (cellW + gapW),
477
+ y: box.y + row * (cellH + gapH),
478
+ cx: cellW,
479
+ cy: cellH
480
+ };
481
+ out.push(...body.length > 1 ? stackCell(parsed, body, point, data, cell, shapeType, i, vertical) : splitCell(parsed, childName, point, data, cell, shapeType, i));
482
+ const last = vertical ? row === rows - 1 : col === inRow - 1;
483
+ if (trans && !last) out.push(connector(trans, point, {
484
+ x: vertical ? cell.x : cell.x + cellW,
485
+ y: vertical ? cell.y + cellH : cell.y,
486
+ cx: vertical ? cellW : gapW,
487
+ cy: vertical ? gapH : cellH
488
+ }, vertical));
489
+ });
490
+ return out;
491
+ }
492
+ /**
493
+ * §21.4.3.2 — the box a list draws in the gap between two cells.
494
+ *
495
+ * `dgm:shape type="conn"` is not a preset geometry, it is "whatever joins these
496
+ * two", and for a list laid out across that is an arrow pointing the way the
497
+ * list runs. Its own constraint sizes it against the gap it was given — the
498
+ * accent process asks for a height of 62% of its width — and it is centred in
499
+ * what is left.
500
+ */
501
+ function connector(node, point, gap, vertical) {
502
+ let { cx, cy } = gap;
503
+ for (const k of node.constrs) {
504
+ if (k.forName !== void 0 || k.refType === void 0) continue;
505
+ if (k.type === "h" && k.refType === "w") cy = Math.min(gap.cy, gap.cx * k.fact);
506
+ if (k.type === "w" && k.refType === "h") cx = Math.min(gap.cx, gap.cy * k.fact);
507
+ }
508
+ return {
509
+ point: {
510
+ id: `${point.id}/${node.name}`,
511
+ type: "node"
512
+ },
513
+ shapeType: node.shapeType === "conn" || node.shapeType === void 0 ? vertical ? "downArrow" : "rightArrow" : node.shapeType,
514
+ ...styleOf(node),
515
+ ...node.styleLbl === void 0 ? { styleLbl: "sibTrans2D1" } : {},
516
+ index: 0,
517
+ x: gap.x + (gap.cx - cx) / 2,
518
+ y: gap.y + (gap.cy - cy) / 2,
519
+ cx,
520
+ cy
521
+ };
522
+ }
523
+ function transitionBox(node) {
524
+ for (const item of node.children) {
525
+ if (item.kind !== "forEach") continue;
526
+ for (const inner of item.children) {
527
+ if (inner.kind !== "forEach" || inner.ptType !== "sibTrans") continue;
528
+ for (const box of inner.children) if (box.kind === "node" && box.shapeType !== void 0 && box.hideGeom !== true) return box;
529
+ }
530
+ }
531
+ }
532
+ /**
533
+ * §21.4.3 — a `forEach` body of SEVERAL boxes is a stack, not a box.
534
+ *
535
+ * The vertical list family gives each point three: the row holding its label,
536
+ * a negative space, and the box its descendants' words go in. Laying out only
537
+ * the first of them drew the labels and dropped every descendant.
538
+ *
539
+ * What each box states for its height divides the cell between them. A space
540
+ * stated as negative is an overlap — one box pulled up over the one before it —
541
+ * which this engine does not run, so it takes no room and the rest share the
542
+ * cell; LibreOffice draws these layouts without the overlap too.
543
+ */
544
+ function stackCell(parsed, body, point, data, cell, shapeType, index, vertical) {
545
+ const stated = body.map((b) => stackFact(parsed, b) ?? 0);
546
+ const total = stated.reduce((a, v) => a + Math.max(0, v), 0);
547
+ const shares = stated.map((v) => total > 0 ? Math.max(0, v) / total : 1 / body.length);
548
+ const out = [];
549
+ let at = vertical ? cell.y : cell.x;
550
+ body.forEach((b, k) => {
551
+ const span = (vertical ? cell.cy : cell.cx) * (shares[k] ?? 0);
552
+ if (span > 0 && !(b.alg === "sp" && b.shapeType === void 0)) {
553
+ const slice = {
554
+ x: vertical ? cell.x : at,
555
+ y: vertical ? at : cell.y,
556
+ cx: vertical ? cell.cx : span,
557
+ cy: vertical ? span : cell.cy
558
+ };
559
+ out.push(...splitCell(parsed, b.name, point, data, slice, b.shapeType ?? shapeType, index));
560
+ }
561
+ at += span;
562
+ });
563
+ return out;
564
+ }
565
+ /**
566
+ * The boxes a wrapper INSETS, each at the width it states and where it falls.
567
+ *
568
+ * Widths that add up to the whole are a division and belong to the split; ones
569
+ * that fall short are placements — a margin, then a label of seven tenths, then
570
+ * nothing. Undefined when the boxes do not all state a width, or when they fill
571
+ * the row between them.
572
+ */
573
+ function insetBoxes(parent, child, width) {
574
+ const boxes = namedBoxes(child);
575
+ if (boxes.length < 2) return void 0;
576
+ const facts = boxes.map((b) => sizeFact(child.constrs, b.name, "w") ?? sizeFact(parent.constrs, b.name, "w"));
577
+ if (facts.some((v) => v === void 0)) return void 0;
578
+ if (facts.reduce((a, v) => a + (v ?? 0), 0) > .999) return void 0;
579
+ const out = [];
580
+ let at = 0;
581
+ boxes.forEach((box, i) => {
582
+ const cx = width * (facts[i] ?? 0);
583
+ out.push({
584
+ box,
585
+ x: at,
586
+ cx
587
+ });
588
+ at += cx;
589
+ });
590
+ return out;
591
+ }
592
+ function stackFact(parsed, box) {
593
+ const own = heightFact(parsed, box.name) ?? sizeFact(parsed.constrs, box.name, "h");
594
+ if (own !== void 0) return own;
595
+ for (const inner of namedBoxes(box)) {
596
+ const f = heightFact(parsed, inner.name) ?? sizeFact(parsed.constrs, inner.name, "h");
597
+ if (f !== void 0) return f;
598
+ }
599
+ }
600
+ /**
601
+ * One data node's cell, split the way the child layoutNode divides it.
602
+ *
603
+ * A leaf child (`tx` on the node itself) takes the cell whole. A child that
604
+ * nests further hands its own children a share of the cell, and the two
605
+ * families that do differ in which way they cut it:
606
+ *
607
+ * - the `linNode` of the vertical block lists states `w` fractions for its
608
+ * children (0.36 for the label, 0.64 for the descendants' text) — a row;
609
+ * - the `composite` of the picture and colour lists gives both children the
610
+ * full width and states their `h` against a font size (0.8 against 1.22) —
611
+ * a column.
612
+ *
613
+ * Laid out as one box either way, such a cell holds one text where it should
614
+ * hold the node's label and its children's text in boxes of their own.
615
+ */
616
+ function splitCell(parent, childName, point, data, cell, shapeType, index) {
617
+ const child = findNamed(parent.children, childName);
618
+ const inner = (child ? namedBoxes(child) : []).filter((b) => !(b.alg === "sp" && b.hideGeom === true));
619
+ const size = (box, type) => box === void 0 ? void 0 : fontSize(box.constrs, void 0, type) ?? fontSize(child?.constrs ?? [], box.name, type) ?? fontSize(parent.constrs, box.name, type);
620
+ const face = inner.length === 1 ? inner[0] : child;
621
+ const whole = () => [{
622
+ point: face === void 0 ? point : spokenFor(face, point, data),
623
+ shapeType: face?.shapeType ?? shapeType,
624
+ ...face?.styleLbl !== void 0 ? { styleLbl: face.styleLbl } : {},
625
+ ...opt("fontSizePt", size(face, "primFontSz")),
626
+ ...face !== void 0 && bulleted(face) ? { bulleted: true } : {},
627
+ ...face?.hideGeom === true ? { hideGeom: true } : {},
628
+ ...face === void 0 ? {} : textPlacing(face),
629
+ index,
630
+ ...cell
631
+ }];
632
+ if (child?.alg === "composite" && inner.length > 0 && places(child.constrs)) return compositeLayout(child, point, cell, data, index, parent.constrs);
633
+ const inset = child ? insetBoxes(parent, child, cell.cx) : void 0;
634
+ if (child && inset) {
635
+ const out = [];
636
+ for (const { box, x, cx } of inset) {
637
+ if (box.alg === "sp" && box.hideGeom === true) continue;
638
+ out.push(...splitCell(parent, box.name, point, data, {
639
+ ...cell,
640
+ x: cell.x + x,
641
+ cx
642
+ }, box.shapeType ?? shapeType, index));
643
+ }
644
+ if (out.length > 0) return out;
645
+ }
646
+ if (!child || inner.length < 2) return whole();
647
+ const widths = inner.map((b) => sizeFact(child.constrs, b.name, "w"));
648
+ const across = widths.some((v) => v !== void 0 && v < .99);
649
+ const share = widths.reduce((a, v) => a + (v ?? 0), 0);
650
+ if (inner.length > 2 && !(across && widths.every((v) => v !== void 0) && Math.abs(share - 1) < .02)) return whole();
651
+ const kids = data.children(point.id, "node");
652
+ const shares = across ? fractions(widths) : fractions(inner.map((b) => sizeFact(child.constrs, b.name, "h") ?? heightFact(parent, b.name)));
653
+ const cap = inner.length === 2 ? heightCap(inner[0], across ? cell.cx * (shares[0] ?? 1) : cell.cx) : void 0;
654
+ if (!across && cap !== void 0) {
655
+ shares[0] = Math.min(cell.cy * (shares[0] ?? .5), cap) / cell.cy;
656
+ shares[1] = 1 - shares[0];
657
+ }
658
+ const out = [];
659
+ let at = across ? cell.x : cell.y;
660
+ inner.forEach((box, i) => {
661
+ const span = (across ? cell.cx : cell.cy) * (shares[i] ?? .5);
662
+ const spacer = box.alg === "sp" && box.shapeType === void 0;
663
+ if (span > 0 && !spacer && !(box.needsChildren && kids.length === 0)) out.push({
664
+ point: box.alg === "sp" ? {
665
+ id: `${point.id}/${box.name}`,
666
+ type: "node"
667
+ } : box.presOfAxis === void 0 ? i === 0 ? point : gatheredText(point, kids) : spokenFor(box, point, data),
668
+ shapeType: i === 0 ? shapeType : box.shapeType ?? "rect",
669
+ ...box.styleLbl !== void 0 ? { styleLbl: box.styleLbl } : {},
670
+ ...opt("fontSizePt", size(box, i === 0 ? "primFontSz" : "secFontSz")),
671
+ ...bulleted(box) ? { bulleted: true } : {},
672
+ ...box.hideGeom === true ? { hideGeom: true } : {},
673
+ ...textPlacing(box),
674
+ index,
675
+ x: across ? at : cell.x,
676
+ y: across ? cell.y : at,
677
+ cx: across ? span : cell.cx,
678
+ cy: across ? cell.cy : span
679
+ });
680
+ at += span;
681
+ });
682
+ return out.length > 0 ? out : whole();
683
+ }
684
+ function fractions(facts) {
685
+ const total = facts.reduce((a, b) => a + (b ?? 0), 0);
686
+ if (total > 1.001) return facts.map((v) => (v ?? 0) / total);
687
+ const unstated = facts.filter((v) => v === void 0).length;
688
+ const rest = Math.max(0, 1 - total) / Math.max(1, unstated);
689
+ return facts.map((v) => v ?? rest);
690
+ }
691
+ function gatheredText(parent, kids) {
692
+ const paragraphs = kids.flatMap((k) => k.text ? poChildren(k.text) : []);
693
+ if (paragraphs.length === 0) return {
694
+ id: `${parent.id}/des`,
695
+ type: "node"
696
+ };
697
+ return {
698
+ id: `${parent.id}/des`,
699
+ type: "node",
700
+ text: { "dgm:t": paragraphs }
701
+ };
702
+ }
703
+ function bestColumns(n, cx, cy, aspect, gap) {
704
+ if (n <= 1) return 1;
705
+ let best = n;
706
+ let bestErr = Infinity;
707
+ for (let cols = 1; cols <= n; cols++) {
708
+ const rows = Math.ceil(n / cols);
709
+ const w = cx / (cols + Math.max(0, cols - 1) * gap);
710
+ const h = cy / (rows + Math.max(0, rows - 1) * gap);
711
+ const err = Math.abs(Math.log(h / w / (aspect || 1)));
712
+ if (err < bestErr - 1e-9) {
713
+ bestErr = err;
714
+ best = cols;
715
+ }
716
+ }
717
+ return best;
718
+ }
719
+ function findNamed(items, name) {
720
+ for (const item of items) if (item.kind === "node") {
721
+ if (item.name === name) return item;
722
+ const inner = findNamed(item.children, name);
723
+ if (inner) return inner;
724
+ } else {
725
+ const inner = findNamed(item.children, name);
726
+ if (inner) return inner;
727
+ }
728
+ }
729
+ function namedBoxes(node) {
730
+ const out = [];
731
+ for (const item of node.children) if (item.kind === "node") out.push(item);
732
+ else for (const inner of item.children) if (inner.kind === "node") out.push(inner);
733
+ return out;
734
+ }
735
+ function sizeFact(constrs, name, type) {
736
+ for (const k of constrs) if (k.type === type && k.forName === name && k.refType === type && k.refForName === void 0) return k.fact;
737
+ }
738
+ function fontSize(constrs, name, type) {
739
+ for (const k of constrs) {
740
+ if (k.type !== type || k.forName !== name) continue;
741
+ if (k.val !== void 0) return k.val;
742
+ if (k.refType === "primFontSz" && k.refForName !== void 0) {
743
+ const from = fontSize(constrs, k.refForName, "primFontSz");
744
+ if (from !== void 0) return from * (k.fact || 1);
745
+ }
746
+ }
747
+ return type === "secFontSz" ? fontSize(constrs, name, "primFontSz") : void 0;
748
+ }
749
+ function bulleted(node) {
750
+ return Number(node.algParams.get("stBulletLvl") ?? "0") >= 1;
751
+ }
752
+ function opt(name, v) {
753
+ return v === void 0 ? {} : { [name]: v };
754
+ }
755
+ function heightCap(box, widthEmu) {
756
+ for (const k of box?.constrs ?? []) if (k.type === "h" && k.refType === "w" && k.op === "lte") return widthEmu * k.fact;
757
+ }
758
+ function heightFact(parent, name) {
759
+ for (const k of parent.constrs) if (k.type === "h" && k.forName === name && k.refType === "primFontSz") return k.fact;
760
+ }
761
+ function resolve(constrs, childName) {
762
+ let aspect = .6;
763
+ let gap = .1;
764
+ for (const k of constrs) {
765
+ if (k.forName === childName && k.type === "h" && k.refType === "w") aspect = k.fact;
766
+ if (k.type === "w" && k.refForName === childName && k.forName !== childName) gap = k.fact;
767
+ }
768
+ return {
769
+ aspect,
770
+ gap
771
+ };
772
+ }
773
+ function isVertical(node) {
774
+ if (node.algParams.get("flowDir") === "col") return true;
775
+ const dir = node.algParams.get("linDir");
776
+ return dir === "fromT" || dir === "fromB";
777
+ }
778
+ function firstNodeName(items) {
779
+ for (const item of items) {
780
+ if (item.kind === "node") return item.name;
781
+ const inner = firstNodeName(item.children);
782
+ if (inner !== void 0) return inner;
783
+ }
784
+ }
785
+ function childShapeType(items) {
786
+ for (const item of items) {
787
+ if (item.kind === "node") return item.shapeType ?? childShapeType(item.children);
788
+ const inner = childShapeType(item.children);
789
+ if (inner !== void 0) return inner;
790
+ }
791
+ }
792
+ /**
793
+ * §21.4.3.3 — a `choose` picks one branch by a function of the data, and what
794
+ * the branch holds belongs to whatever the `choose` was standing in. The
795
+ * direction variables are left at their defaults, so those take the first
796
+ * `if`; `maxDepth` is answered from the model, because the branch it guards is
797
+ * the difference between one box in a cell and two.
798
+ *
799
+ * @returns The children with every `choose` replaced by the branch taken, and
800
+ * the ones whose branch was guarded on a child count.
801
+ */
802
+ function flattenChoose(kids, ctx) {
803
+ const flat = [];
804
+ const guarded = /* @__PURE__ */ new Set();
805
+ for (const k of kids) {
806
+ if (!poIs(k, "dgm:choose")) {
807
+ flat.push(k);
808
+ continue;
809
+ }
810
+ const branches = poChildren(k);
811
+ const taken = branches.find((b) => poIs(b, "dgm:if") && holds(b, ctx)) ?? branches.find((b) => poIs(b, "dgm:else")) ?? branches[0];
812
+ const onCount = taken !== void 0 && poIs(taken, "dgm:if") && countsChildren(taken);
813
+ const inner = flattenChoose(poChildren(taken), ctx);
814
+ for (const item of inner.flat) {
815
+ flat.push(item);
816
+ if (onCount || inner.guarded.has(item)) guarded.add(item);
817
+ }
818
+ }
819
+ return {
820
+ flat,
821
+ guarded
822
+ };
823
+ }
824
+ function parseNode(node, ctx) {
825
+ const { flat: flattened, guarded } = flattenChoose(poChildren(node), ctx);
826
+ const alg = flattened.find((k) => poIs(k, "dgm:alg"));
827
+ const shape = flattened.find((k) => poIs(k, "dgm:shape"));
828
+ const constrLst = flattened.find((k) => poIs(k, "dgm:constrLst"));
829
+ const params = /* @__PURE__ */ new Map();
830
+ for (const p of alg ? poChildren(alg) : []) {
831
+ const t = poAttr(p, "type");
832
+ const v = poAttr(p, "val");
833
+ if (t !== void 0 && v !== void 0) params.set(t, v);
834
+ }
835
+ const shapeType = shape ? poAttr(shape, "type") : void 0;
836
+ const styleLbl = poAttr(node, "styleLbl");
837
+ const presOf = flattened.find((k) => poIs(k, "dgm:presOf"));
838
+ const presOfAxis = poAttr(presOf, "axis");
839
+ const presOfPath = axisPath(presOf);
840
+ const shapeRot = shape ? Number(poAttr(shape, "rot") ?? "") : NaN;
841
+ return {
842
+ kind: "node",
843
+ name: poAttr(node, "name") ?? "",
844
+ ...styleLbl !== void 0 && styleLbl !== "" ? { styleLbl } : {},
845
+ needsChildren: false,
846
+ alg: algOf(alg),
847
+ algParams: params,
848
+ ...shapeType !== void 0 && shapeType !== "" ? { shapeType } : {},
849
+ ...shape !== void 0 && poAttr(shape, "hideGeom") === "1" ? { hideGeom: true } : {},
850
+ ...Number.isFinite(shapeRot) && shapeRot !== 0 ? { shapeRotation: shapeRot } : {},
851
+ ...presOfAxis !== void 0 && presOfAxis !== "" ? { presOfAxis } : {},
852
+ ...presOfPath ? { presOfPath } : {},
853
+ constrs: parseConstraints(constrLst),
854
+ children: flattened.flatMap((k) => parseItem(k, ctx, guarded.has(k)))
855
+ };
856
+ }
857
+ function holds(branch, ctx) {
858
+ const func = poAttr(branch, "func");
859
+ const axis = (poAttr(branch, "axis") ?? "").split(/\s+/u).filter((a) => a !== "");
860
+ if (func === "maxDepth") return compare(ctx.maxDepth, branch, "gte");
861
+ if (func === "cnt" && axis.length > 1) return compare(countAt(axis, branch, ctx), branch, "gte");
862
+ return true;
863
+ }
864
+ function countAt(axis, branch, ctx) {
865
+ const nums = (name) => (poAttr(branch, name) ?? "").split(/\s+/u).map((v) => Number(v));
866
+ const st = nums("st");
867
+ const cnt = nums("cnt");
868
+ const types = (poAttr(branch, "ptType") ?? "").split(/\s+/u);
869
+ let at = [ctx.root];
870
+ axis.forEach((step, i) => {
871
+ if (step !== "ch" && step !== "des") {
872
+ at = [];
873
+ return;
874
+ }
875
+ if ((types[i] === "node" || types[i] === void 0 ? "node" : void 0) === void 0) {
876
+ at = [];
877
+ return;
878
+ }
879
+ const next = at.flatMap((p) => ctx.data.children(p.id, "node"));
880
+ const from = Math.max(0, (st[i] ?? 1) - 1);
881
+ const want = cnt[i];
882
+ const take = want === void 0 || want === 0 ? next.length : want;
883
+ at = next.slice(from, from + take);
884
+ });
885
+ return at.length;
886
+ }
887
+ function compare(actual, branch, fallback) {
888
+ const val = Number(poAttr(branch, "val") ?? "0");
889
+ const op = poAttr(branch, "op") ?? fallback;
890
+ if (op === "gte") return actual >= val;
891
+ if (op === "gt") return actual > val;
892
+ if (op === "lte") return actual <= val;
893
+ if (op === "lt") return actual < val;
894
+ if (op === "neq") return actual !== val;
895
+ return actual === val;
896
+ }
897
+ function countsChildren(branch) {
898
+ return poAttr(branch, "func") === "cnt" && poAttr(branch, "axis") === "ch" && poAttr(branch, "ptType") === "node" && (poAttr(branch, "op") ?? "gte") === "gte" && Number(poAttr(branch, "val") ?? "1") >= 1;
899
+ }
900
+ function parseItem(node, ctx, guarded = false) {
901
+ if (poIs(node, "dgm:layoutNode")) return [{
902
+ ...parseNode(node, ctx),
903
+ needsChildren: guarded
904
+ }];
905
+ if (poIs(node, "dgm:forEach")) {
906
+ const inner = flattenChoose(poChildren(node), ctx);
907
+ return [{
908
+ kind: "forEach",
909
+ axis: poAttr(node, "axis") ?? "",
910
+ ptType: poAttr(node, "ptType") ?? "",
911
+ children: inner.flat.flatMap((k) => parseItem(k, ctx, guarded || inner.guarded.has(k)))
912
+ }];
913
+ }
914
+ return [];
915
+ }
916
+ function parseConstraints(list) {
917
+ const out = [];
918
+ for (const k of list ? poChildren(list) : []) {
919
+ if (!poIs(k, "dgm:constr")) continue;
920
+ const type = poAttr(k, "type");
921
+ if (type === void 0) continue;
922
+ const forName = poAttr(k, "forName");
923
+ const op = poAttr(k, "op");
924
+ const refType = poAttr(k, "refType");
925
+ const refForName = poAttr(k, "refForName");
926
+ const val = poAttr(k, "val");
927
+ out.push({
928
+ type,
929
+ ...forName !== void 0 ? { forName } : {},
930
+ ...op !== void 0 ? { op } : {},
931
+ ...refType !== void 0 ? { refType } : {},
932
+ ...refForName !== void 0 ? { refForName } : {},
933
+ fact: Number(poAttr(k, "fact") ?? "1"),
934
+ ...val !== void 0 ? { val: Number(val) } : {}
935
+ });
936
+ }
937
+ return out;
938
+ }
939
+ function algOf(alg) {
940
+ const t = alg ? poAttr(alg, "type") : void 0;
941
+ if (t === "hierRoot" || t === "hierChild") return "hier";
942
+ return t === "snake" || t === "lin" || t === "sp" || t === "tx" || t === "composite" || t === "cycle" ? t : "other";
943
+ }
944
+ function findChild(nodes, tag) {
945
+ return nodes.find((n) => poIs(n, tag) || poTag(n) === tag);
946
+ }
947
+ //#endregion
948
+ export { layoutDiagram };