@capyclaw/ppt-workbench 0.0.0-stage → 2.4.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 (130) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +126 -3
  3. package/THIRD_PARTY_NOTICES.md +21 -0
  4. package/contract/ppt-contract.json +1529 -0
  5. package/cordis.patch.yml +25 -0
  6. package/core/audit.js +500 -0
  7. package/core/chart.js +199 -0
  8. package/core/color.js +80 -0
  9. package/core/deck-layout.js +195 -0
  10. package/core/formula.js +80 -0
  11. package/core/hfmath/hershey.js +1763 -0
  12. package/core/hfmath/index.js +1176 -0
  13. package/core/hfmath/stretch.js +41 -0
  14. package/core/hfmath/symbols.js +408 -0
  15. package/core/ops/browser.js +28 -0
  16. package/core/ops/chart.js +64 -0
  17. package/core/ops/deck.js +21 -0
  18. package/core/ops/dispatcher.js +2 -0
  19. package/core/ops/element.js +55 -0
  20. package/core/ops/history.js +11 -0
  21. package/core/ops/import.js +95 -0
  22. package/core/ops/index.js +39 -0
  23. package/core/ops/playback.js +9 -0
  24. package/core/ops/recolor.js +33 -0
  25. package/core/ops/slide.js +66 -0
  26. package/core/ops/table.js +85 -0
  27. package/core/ops/text.js +35 -0
  28. package/core/ops/view.js +13 -0
  29. package/core/ops.js +12 -0
  30. package/core/quick.js +52 -0
  31. package/core/shape-library.js +122 -0
  32. package/core/table.js +178 -0
  33. package/core/text.js +200 -0
  34. package/editor/app.js +5366 -0
  35. package/editor/heartbeat-worker.js +116 -0
  36. package/editor/index.html +89 -0
  37. package/editor/ppt-full-app.js +376 -0
  38. package/lib/host.mjs +98 -0
  39. package/package.json +51 -4
  40. package/server/audit.mjs +2 -0
  41. package/server/bridge-client.mjs +174 -0
  42. package/server/constants.mjs +20 -0
  43. package/server/contract-snapshots.mjs +62 -0
  44. package/server/contract.mjs +24 -0
  45. package/server/deck-layout.mjs +2 -0
  46. package/server/http-utils.mjs +2 -0
  47. package/server/infra/http.mjs +113 -0
  48. package/server/infra/runtime/context.mjs +128 -0
  49. package/server/infra/store/blob-store.mjs +109 -0
  50. package/server/infra/tuning.mjs +82 -0
  51. package/server/infra/util/fs-lock.mjs +132 -0
  52. package/server/infra/workspace/manager.mjs +657 -0
  53. package/server/mcp/dispatch.mjs +2 -0
  54. package/server/mcp/handler.mjs +2 -0
  55. package/server/mcp/instructions.mjs +40 -0
  56. package/server/mcp-http.mjs +3 -0
  57. package/server/ppt-shim.mjs +156 -0
  58. package/server/relay.mjs +245 -0
  59. package/server/routes/assets.mjs +2 -0
  60. package/server/routes/bridge.mjs +47 -0
  61. package/server/routes/editor.mjs +9 -0
  62. package/server/routes/import.mjs +258 -0
  63. package/server/routes/mcp.mjs +17 -0
  64. package/server/runtime/recovery.mjs +9 -0
  65. package/server/runtime/watch.mjs +17 -0
  66. package/server/shared/SHARED_MANIFEST.json +47 -0
  67. package/server/shared/asset-service.mjs +217 -0
  68. package/server/shared/editor-host.mjs +125 -0
  69. package/server/shared/kit/editor/lease.mjs +139 -0
  70. package/server/shared/kit/service/api.mjs +45 -0
  71. package/server/shared/kit/service/http.mjs +113 -0
  72. package/server/shared/kit/service/mcp-http.mjs +142 -0
  73. package/server/shared/kit/service/single-instance.mjs +72 -0
  74. package/server/shared/kit/tools/validate.mjs +115 -0
  75. package/server/shared/kit/tuning.mjs +82 -0
  76. package/server/shared/kit/util/fs-lock.mjs +132 -0
  77. package/server/shared/service-host.mjs +511 -0
  78. package/server/shared/state-service.mjs +249 -0
  79. package/server/shared/tool-host.mjs +341 -0
  80. package/server/shared/tuning.mjs +63 -0
  81. package/server/single-instance.mjs +2 -0
  82. package/server/store/backends/file-backend.mjs +13 -0
  83. package/server/store/checkpoints.mjs +28 -0
  84. package/server/store/doc.mjs +52 -0
  85. package/server/store/index.mjs +34 -0
  86. package/server/store/journal.mjs +9 -0
  87. package/server/store/lease.mjs +2 -0
  88. package/server/store/sanitize.mjs +32 -0
  89. package/server/store.mjs +2 -0
  90. package/server/tools/capabilities/apply.mjs +33 -0
  91. package/server/tools/capabilities/audit.mjs +24 -0
  92. package/server/tools/capabilities/chart.mjs +83 -0
  93. package/server/tools/capabilities/compose.mjs +97 -0
  94. package/server/tools/capabilities/contract.mjs +34 -0
  95. package/server/tools/capabilities/deck.mjs +27 -0
  96. package/server/tools/capabilities/element.mjs +67 -0
  97. package/server/tools/capabilities/export.mjs +95 -0
  98. package/server/tools/capabilities/formula.mjs +72 -0
  99. package/server/tools/capabilities/history.mjs +29 -0
  100. package/server/tools/capabilities/image.mjs +152 -0
  101. package/server/tools/capabilities/imagesearch.mjs +152 -0
  102. package/server/tools/capabilities/import.mjs +180 -0
  103. package/server/tools/capabilities/index.mjs +58 -0
  104. package/server/tools/capabilities/line.mjs +58 -0
  105. package/server/tools/capabilities/notes.mjs +33 -0
  106. package/server/tools/capabilities/present.mjs +34 -0
  107. package/server/tools/capabilities/recolor.mjs +60 -0
  108. package/server/tools/capabilities/screenshot.mjs +86 -0
  109. package/server/tools/capabilities/shape.mjs +64 -0
  110. package/server/tools/capabilities/slide.mjs +81 -0
  111. package/server/tools/capabilities/style.mjs +48 -0
  112. package/server/tools/capabilities/table.mjs +127 -0
  113. package/server/tools/capabilities/task.mjs +52 -0
  114. package/server/tools/capabilities/text.mjs +82 -0
  115. package/server/tools/domains/color.mjs +4 -0
  116. package/server/tools/domains/image.mjs +49 -0
  117. package/server/tools/domains/index.mjs +7 -0
  118. package/server/tools/domains/pptx-export.mjs +566 -0
  119. package/server/tools/domains/recolor.mjs +25 -0
  120. package/server/tools/domains/remote-image.mjs +291 -0
  121. package/server/tools/domains/slide.mjs +21 -0
  122. package/server/tools/domains/text-style.mjs +11 -0
  123. package/server/tools/domains/tooling.mjs +13 -0
  124. package/server/tools/index.mjs +14 -0
  125. package/server/tools/kernel/define.mjs +5 -0
  126. package/server/tools/kernel/ports.mjs +42 -0
  127. package/server/tools/kernel/registry.mjs +17 -0
  128. package/server/tools.mjs +18 -0
  129. package/server/tuning.mjs +86 -0
  130. package/server/validate.mjs +2 -0
@@ -0,0 +1,1176 @@
1
+ import { HERSHEY } from "./hershey.js";
2
+ import { SYMB, asciiMap } from "./symbols.js";
3
+ import { stretch } from "./stretch.js";
4
+ const CONFIG = {
5
+ SUB_SUP_SCALE: 0.75,
6
+ SQRT_MAG_SCALE: 0.5,
7
+ FRAC_SCALE: 0.85,
8
+ LINE_SPACING: 0.5,
9
+ FRAC_SPACING: 0.4
10
+ };
11
+ const MATRIX_DELIMITERS = /* @__PURE__ */ new Map([
12
+ ["matrix", []],
13
+ ["bmatrix", ["[", "]"]],
14
+ ["pmatrix", ["(", ")"]],
15
+ ["Bmatrix", ["\\{", "\\}"]],
16
+ ["vmatrix", ["\\lvert", "\\rvert"]],
17
+ ["Vmatrix", ["\\lVert", "\\rVert"]],
18
+ ["cases", ["\\{"]]
19
+ ]);
20
+ function tokenize(str) {
21
+ return str.match(/\\[A-Za-z]+|\\[^\r\n]|[A-Za-z0-9.]+|[^\s]/g) ?? [];
22
+ }
23
+ function parseAtom(x) {
24
+ return { type: SYMB[x] ? "symb" : "char", mode: "math", text: x, chld: [], bbox: null };
25
+ }
26
+ function parse(tokens) {
27
+ const input = tokens.slice();
28
+ let i = 0;
29
+ function empty() {
30
+ return { type: "node", text: "", mode: "math", chld: [], bbox: null };
31
+ }
32
+ function delimited(open, close) {
33
+ const start = ++i;
34
+ let depth = 1;
35
+ let braces = 0;
36
+ while (i < input.length) {
37
+ const token = input[i];
38
+ if (open === "[" && token === "{") braces++;
39
+ if (open === "[" && token === "}") braces--;
40
+ if (!braces && token === open) depth++;
41
+ if (!braces && token === close && --depth === 0) break;
42
+ i++;
43
+ }
44
+ const result = parse(input.slice(start, i));
45
+ if (i < input.length) i++;
46
+ return result;
47
+ }
48
+ function atom(argument = false) {
49
+ if (i >= input.length || argument && input[i] === "}") return empty();
50
+ if (input[i] === "{") {
51
+ const child = delimited("{", "}");
52
+ if (argument) return child;
53
+ return { ...empty(), chld: [child] };
54
+ }
55
+ let token = input[i++];
56
+ if (argument && /^[A-Za-z0-9.]{2,}$/.test(token)) {
57
+ input.splice(i, 0, token.slice(1));
58
+ token = token[0];
59
+ }
60
+ if (token === "\\operatorname" && input[i] === "*") {
61
+ token += input[i++];
62
+ }
63
+ const expr2 = parseAtom(token);
64
+ const symbol = SYMB[token];
65
+ if (symbol?.arity) {
66
+ expr2.type = "func";
67
+ const optional = symbol.flags.opt && input[i] === "[" ? delimited("[", "]") : null;
68
+ for (let n = 0; n < symbol.arity; n++) expr2.chld.push(atom(true));
69
+ if (optional) expr2.chld.push(optional);
70
+ }
71
+ if (token === "\\begin" && expr2.chld[0]?.text === "array" && input[i] === "{") {
72
+ let end = i + 1;
73
+ while (end < input.length && /^[lcr]+$/.test(input[end])) end++;
74
+ if (end > i + 1 && input[end] === "}") {
75
+ expr2.columns = input.slice(i + 1, end).join("");
76
+ i = end + 1;
77
+ }
78
+ }
79
+ if (symbol?.flags.brace) {
80
+ while (input[i] === "^" || input[i] === "_") expr2.chld.push(atom());
81
+ }
82
+ return expr2;
83
+ }
84
+ const expr = empty();
85
+ while (i < input.length) expr.chld.push(atom());
86
+ return expr.chld.length === 1 ? expr.chld[0] : expr;
87
+ }
88
+ function environments(exprs) {
89
+ let i = 0;
90
+ while (i < exprs.length) {
91
+ if (exprs[i].text === "\\begin") {
92
+ let j = i + 1;
93
+ let depth = 1;
94
+ for (; j < exprs.length; j++) {
95
+ if (exprs[j].text === "\\begin") depth++;
96
+ if (exprs[j].text === "\\end" && --depth === 0) break;
97
+ }
98
+ if (j === exprs.length) {
99
+ i++;
100
+ continue;
101
+ }
102
+ const es = exprs.splice(i + 1, j - (i + 1));
103
+ environments(es);
104
+ exprs[i].text = exprs[i].chld[0].text;
105
+ exprs[i].chld = es;
106
+ exprs.splice(i + 1, 1);
107
+ } else {
108
+ environments(exprs[i].chld);
109
+ }
110
+ i++;
111
+ }
112
+ }
113
+ function transform(expr, sclx, scly, x, y, notFirst) {
114
+ if (scly === null || scly === void 0) {
115
+ scly = sclx;
116
+ }
117
+ if (!expr.bbox) return;
118
+ if (notFirst) {
119
+ expr.bbox.x *= sclx;
120
+ expr.bbox.y *= scly;
121
+ }
122
+ expr.bbox.w *= sclx;
123
+ expr.bbox.h *= scly;
124
+ for (let i = 0; i < expr.chld.length; i++) {
125
+ transform(expr.chld[i], sclx, scly, 0, 0, true);
126
+ }
127
+ expr.bbox.x += x;
128
+ expr.bbox.y += y;
129
+ }
130
+ function computeBbox(exprs) {
131
+ let xmin = Infinity;
132
+ let xmax = -Infinity;
133
+ let ymin = Infinity;
134
+ let ymax = -Infinity;
135
+ for (let i = 0; i < exprs.length; i++) {
136
+ if (!exprs[i].bbox) {
137
+ continue;
138
+ }
139
+ xmin = Math.min(xmin, exprs[i].bbox.x);
140
+ ymin = Math.min(ymin, exprs[i].bbox.y);
141
+ xmax = Math.max(xmax, exprs[i].bbox.x + exprs[i].bbox.w);
142
+ ymax = Math.max(ymax, exprs[i].bbox.y + exprs[i].bbox.h);
143
+ }
144
+ return xmin === Infinity ? { x: 0, y: 0, w: 0, h: 0 } : { x: xmin, y: ymin, w: xmax - xmin, h: ymax - ymin };
145
+ }
146
+ function group(exprs) {
147
+ if (!exprs.length) {
148
+ return null;
149
+ }
150
+ const bbox = computeBbox(exprs);
151
+ for (let i = 0; i < exprs.length; i++) {
152
+ if (!exprs[i].bbox) {
153
+ continue;
154
+ }
155
+ exprs[i].bbox.x -= bbox.x;
156
+ exprs[i].bbox.y -= bbox.y;
157
+ }
158
+ const expr = {
159
+ type: "node",
160
+ text: "",
161
+ mode: "math",
162
+ chld: exprs,
163
+ bbox
164
+ };
165
+ return expr;
166
+ }
167
+ function align(exprs, alignment = "center") {
168
+ for (let i = 0; i < exprs.length; i++) {
169
+ if (exprs[i].text === "^" || exprs[i].text === "'") {
170
+ let h = 0;
171
+ let j = i;
172
+ while (j > 0 && (exprs[j].text === "^" || exprs[j].text === "_" || exprs[j].text === "'")) {
173
+ j--;
174
+ }
175
+ h = exprs[j].bbox.y;
176
+ if (exprs[i].text === "'") {
177
+ exprs[i].bbox.y = h;
178
+ } else {
179
+ transform(exprs[i], CONFIG.SUB_SUP_SCALE, null, 0, 0);
180
+ if (SYMB[exprs[j].text] && SYMB[exprs[j].text].flags.big) {
181
+ exprs[i].bbox.y = h - exprs[i].bbox.h;
182
+ } else if (SYMB[exprs[j].text]?.flags.integral) {
183
+ exprs[i].bbox.y = h;
184
+ } else {
185
+ exprs[i].bbox.y = h - exprs[i].bbox.h / 2;
186
+ }
187
+ }
188
+ } else if (exprs[i].text === "_") {
189
+ let h = 1;
190
+ let j = i;
191
+ while (j > 0 && (exprs[j].text === "^" || exprs[j].text === "_" || exprs[j].text === "'")) {
192
+ j--;
193
+ }
194
+ h = exprs[j].bbox.y + exprs[j].bbox.h;
195
+ transform(exprs[i], CONFIG.SUB_SUP_SCALE, null, 0, 0);
196
+ if (SYMB[exprs[j].text] && SYMB[exprs[j].text].flags.big) {
197
+ exprs[i].bbox.y = h;
198
+ } else if (SYMB[exprs[j].text]?.flags.integral) {
199
+ exprs[i].bbox.y = h - exprs[i].bbox.h;
200
+ } else {
201
+ exprs[i].bbox.y = h - exprs[i].bbox.h / 2;
202
+ }
203
+ }
204
+ }
205
+ function searchHigh(i, l, r, dir, lvl0) {
206
+ let j = i;
207
+ let lvl = lvl0;
208
+ let ymin = Infinity;
209
+ let ymax = -Infinity;
210
+ while (dir > 0 ? j < exprs.length : j >= 0) {
211
+ if (exprs[j].text === l) {
212
+ lvl++;
213
+ } else if (exprs[j].text === r) {
214
+ lvl--;
215
+ if (lvl === 0) {
216
+ break;
217
+ }
218
+ } else if (exprs[j].text === "^" || exprs[j].text === "_") {
219
+ } else if (exprs[j].bbox) {
220
+ ymin = Math.min(ymin, exprs[j].bbox.y);
221
+ ymax = Math.max(ymax, exprs[j].bbox.y + exprs[j].bbox.h);
222
+ }
223
+ j += dir;
224
+ }
225
+ return [ymin, ymax];
226
+ }
227
+ for (let i = 0; i < exprs.length; i++) {
228
+ if (exprs[i].text === "\\left") {
229
+ const [ymin, ymax] = searchHigh(i, "\\left", "\\right", 1, 0);
230
+ if (ymin !== Infinity && ymax !== -Infinity) {
231
+ exprs[i].bbox.y = ymin;
232
+ transform(exprs[i], 1, (ymax - ymin) / exprs[i].bbox.h, 0, 0);
233
+ }
234
+ } else if (exprs[i].text === "\\right") {
235
+ const [ymin, ymax] = searchHigh(i, "\\right", "\\left", -1, 0);
236
+ if (ymin !== Infinity && ymax !== -Infinity) {
237
+ exprs[i].bbox.y = ymin;
238
+ transform(exprs[i], 1, (ymax - ymin) / exprs[i].bbox.h, 0, 0);
239
+ }
240
+ } else if (exprs[i].text === "\\middle") {
241
+ const [lmin, lmax] = searchHigh(i, "\\right", "\\left", -1, 1);
242
+ const [rmin, rmax] = searchHigh(i, "\\left", "\\right", 1, 1);
243
+ const ymin = Math.min(lmin, rmin);
244
+ const ymax = Math.max(lmax, rmax);
245
+ if (ymin !== Infinity && ymax !== -Infinity) {
246
+ exprs[i].bbox.y = ymin;
247
+ transform(exprs[i], 1, (ymax - ymin) / exprs[i].bbox.h, 0, 0);
248
+ }
249
+ }
250
+ }
251
+ if (!exprs.some((x) => x.text === "&" || x.text === "\\\\")) {
252
+ return;
253
+ }
254
+ const rows = [];
255
+ let row = [];
256
+ let cell = [];
257
+ for (let i = 0; i < exprs.length; i++) {
258
+ if (exprs[i].text === "&") {
259
+ row.push(cell);
260
+ cell = [];
261
+ } else if (exprs[i].text === "\\\\") {
262
+ row.push(cell);
263
+ cell = [];
264
+ rows.push(row);
265
+ row = [];
266
+ } else {
267
+ cell.push(exprs[i]);
268
+ }
269
+ }
270
+ if (cell.length || row.length) {
271
+ row.push(cell);
272
+ }
273
+ if (row.length) {
274
+ rows.push(row);
275
+ }
276
+ const colws = [];
277
+ const erows = [];
278
+ for (let i = 0; i < rows.length; i++) {
279
+ const erow = [];
280
+ const cells = rows[i].map(group);
281
+ const rowY = cells.reduce((y, cell2) => cell2 ? Math.min(y, cell2.bbox.y) : y, Infinity);
282
+ for (let j = 0; j < cells.length; j++) {
283
+ const e = cells[j] ?? {
284
+ type: "node",
285
+ text: "",
286
+ mode: "math",
287
+ chld: [],
288
+ bbox: { x: 0, y: Number.isFinite(rowY) ? rowY : 0, w: 0, h: 2 }
289
+ };
290
+ colws[j] = Math.max(e.bbox.w + 1, colws[j] ?? 0);
291
+ erow[j] = e;
292
+ }
293
+ erows.push(erow);
294
+ }
295
+ const ybds = [];
296
+ for (let i = 0; i < erows.length; i++) {
297
+ let ymin = Infinity;
298
+ let ymax = -Infinity;
299
+ for (let j = 0; j < erows[i].length; j++) {
300
+ const e = erows[i][j];
301
+ ymin = Math.min(ymin, e.bbox.y);
302
+ ymax = Math.max(ymax, e.bbox.y + e.bbox.h);
303
+ }
304
+ ybds.push([ymin, ymax]);
305
+ }
306
+ for (let i = 0; i < ybds.length; i++) {
307
+ if (!Number.isFinite(ybds[i][0]) || !Number.isFinite(ybds[i][1])) {
308
+ ybds[i][0] = i === 0 ? 0 : ybds[i - 1][1];
309
+ ybds[i][1] = ybds[i][0] + 2;
310
+ }
311
+ }
312
+ for (let i = 1; i < erows.length; i++) {
313
+ const shft = ybds[i - 1][1] - ybds[i][0] + CONFIG.LINE_SPACING;
314
+ for (let j = 0; j < erows[i].length; j++) {
315
+ const e = erows[i][j];
316
+ e.bbox.y += shft;
317
+ }
318
+ ybds[i][0] += shft;
319
+ ybds[i][1] += shft;
320
+ }
321
+ exprs.splice(0, exprs.length);
322
+ for (let i = 0; i < erows.length; i++) {
323
+ let dx = 0;
324
+ for (let j = 0; j < erows[i].length; j++) {
325
+ const e = erows[i][j];
326
+ e.bbox.x += dx;
327
+ dx += colws[j] - e.bbox.w;
328
+ if (alignment === "center") {
329
+ e.bbox.x += (colws[j] - e.bbox.w) / 2;
330
+ } else if (alignment === "left") {
331
+ } else if (alignment === "right") {
332
+ e.bbox.x += colws[j] - e.bbox.w;
333
+ } else if (alignment === "equation") {
334
+ if (j !== erows[i].length - 1) {
335
+ e.bbox.x += colws[j] - e.bbox.w;
336
+ }
337
+ }
338
+ exprs.push(e);
339
+ }
340
+ }
341
+ }
342
+ function planLabel(label, mode) {
343
+ if (!label) return;
344
+ plan(label, mode);
345
+ transform(label, CONFIG.SUB_SUP_SCALE, null, 0, 0);
346
+ if (!label.bbox.w) label.bbox.h = 0;
347
+ }
348
+ function planGroup(expr, mode) {
349
+ const body = expr.chld[0];
350
+ const upper = expr.chld.slice(1).find((child) => child.text === "^");
351
+ const lower = expr.chld.slice(1).find((child) => child.text === "_");
352
+ plan(body, mode);
353
+ planLabel(upper, mode);
354
+ planLabel(lower, mode);
355
+ const bodyY = body.bbox.y;
356
+ const gap = 0.2;
357
+ const height = SYMB[expr.text].flags.brace ? 0.65 : 0.45;
358
+ const decorationWidth = Math.max(body.bbox.w, 1.2);
359
+ const width = Math.max(decorationWidth, upper?.bbox.w ?? 0, lower?.bbox.w ?? 0);
360
+ const above = !SYMB[expr.text].flags.under;
361
+ const top = upper?.bbox.h ? upper.bbox.h + gap : 0;
362
+ const bottom = lower?.bbox.h ? lower.bbox.h + gap : 0;
363
+ const blockHeight = body.bbox.h + height + gap;
364
+ const decoration = {
365
+ type: "stretch",
366
+ text: expr.text,
367
+ mode,
368
+ chld: [],
369
+ bbox: {
370
+ x: (width - decorationWidth) / 2,
371
+ y: top + (above ? 0 : body.bbox.h + gap),
372
+ w: decorationWidth,
373
+ h: height
374
+ }
375
+ };
376
+ body.bbox.x = (width - body.bbox.w) / 2;
377
+ body.bbox.y = top + (above ? height + gap : 0);
378
+ if (upper) {
379
+ upper.bbox.x = (width - upper.bbox.w) / 2;
380
+ upper.bbox.y = 0;
381
+ }
382
+ if (lower) {
383
+ lower.bbox.x = (width - lower.bbox.w) / 2;
384
+ lower.bbox.y = top + blockHeight + gap;
385
+ }
386
+ expr.bbox = { x: 0, y: bodyY - body.bbox.y, w: width, h: top + blockHeight + bottom };
387
+ expr.type = "node";
388
+ expr.chld = [decoration, body, ...[upper, lower].filter((label) => !!label)];
389
+ }
390
+ function planArrow(expr, mode) {
391
+ const [upper, lower] = expr.chld;
392
+ planLabel(upper, mode);
393
+ planLabel(lower, mode);
394
+ const gap = 0.2;
395
+ const side = 0.25;
396
+ const height = 0.6;
397
+ const arrowTop = 1 - height / 2;
398
+ const width = Math.max(3, Math.max(upper?.bbox.w ?? 0, lower?.bbox.w ?? 0) + 1);
399
+ const top = Math.min(0, upper?.bbox.h ? arrowTop - gap - upper.bbox.h : 0);
400
+ const bottom = Math.max(2, lower?.bbox.h ? arrowTop + height + gap + lower.bbox.h : 2);
401
+ const arrow = {
402
+ type: "stretch",
403
+ text: expr.text,
404
+ mode,
405
+ chld: [],
406
+ bbox: { x: side, y: arrowTop - top, w: width, h: height }
407
+ };
408
+ if (upper) {
409
+ upper.bbox.x = side + (width - upper.bbox.w) / 2;
410
+ upper.bbox.y = arrowTop - gap - upper.bbox.h - top;
411
+ }
412
+ if (lower) {
413
+ lower.bbox.x = side + (width - lower.bbox.w) / 2;
414
+ lower.bbox.y = arrowTop + height + gap - top;
415
+ }
416
+ expr.bbox = { x: 0, y: top, w: width + 2 * side, h: bottom - top };
417
+ expr.type = "node";
418
+ expr.chld = [arrow, ...[upper, lower].filter((label) => !!label)];
419
+ }
420
+ function isGrid(expr) {
421
+ return expr.text === "gathered" && expr.type !== "char" || expr.text === "array" && !!expr.columns;
422
+ }
423
+ function planGrid(expr, mode) {
424
+ const gathered = expr.text === "gathered";
425
+ const rows = [];
426
+ let row = [[]];
427
+ let pending = false;
428
+ for (const child of expr.chld) {
429
+ if (child.text === "\\\\") {
430
+ rows.push(row);
431
+ row = [[]];
432
+ pending = false;
433
+ } else if (child.text === "&") {
434
+ if (!gathered) {
435
+ row.push([]);
436
+ pending = true;
437
+ }
438
+ } else {
439
+ row[row.length - 1].push(child);
440
+ pending = true;
441
+ }
442
+ }
443
+ if (pending || !rows.length) rows.push(row);
444
+ const columnCount = rows.reduce((count, cells2) => Math.max(count, cells2.length), expr.columns?.length ?? 1);
445
+ const widths = Array(columnCount).fill(0);
446
+ const cells = rows.map((contents) => Array.from({ length: columnCount }, (_, column) => {
447
+ const cell = { type: "node", text: "", mode, chld: contents[column] ?? [], bbox: null };
448
+ plan(cell, mode, true);
449
+ widths[column] = Math.max(widths[column], cell.bbox.w);
450
+ return cell;
451
+ }));
452
+ const columnGap = 1;
453
+ const width = widths.reduce((sum, value) => sum + value, 0) + columnGap * (columnCount - 1);
454
+ let y = 0;
455
+ for (const cellsInRow of cells) {
456
+ let top = 0;
457
+ let bottom = 2;
458
+ for (const cell of cellsInRow) {
459
+ top = Math.min(top, cell.bbox.y);
460
+ bottom = Math.max(bottom, cell.bbox.y + cell.bbox.h);
461
+ }
462
+ let x = 0;
463
+ for (let column = 0; column < columnCount; column++) {
464
+ const cell = cellsInRow[column];
465
+ const alignment = gathered ? "c" : expr.columns?.[column] ?? "l";
466
+ const remaining = widths[column] - cell.bbox.w;
467
+ cell.bbox.x = x + (alignment === "r" ? remaining : alignment === "c" ? remaining / 2 : 0);
468
+ cell.bbox.y += y - top;
469
+ x += widths[column] + columnGap;
470
+ }
471
+ y += bottom - top + CONFIG.LINE_SPACING;
472
+ }
473
+ const height = y - CONFIG.LINE_SPACING;
474
+ expr.bbox = { x: 0, y: (2 - height) / 2, w: width, h: height };
475
+ expr.type = "node";
476
+ expr.chld = cells.flat();
477
+ }
478
+ function plan(source, mode = "math", preserveBaseline = false) {
479
+ const delimiters = MATRIX_DELIMITERS.get(source.text);
480
+ if (delimiters && !source.chld.length) {
481
+ source.chld.push({ type: "node", text: "", mode: "math", chld: [], bbox: null });
482
+ }
483
+ const expr = source;
484
+ const tmd = {
485
+ "\\text": "text",
486
+ "\\operatorname": "rm",
487
+ "\\operatorname*": "rm",
488
+ "\\mathnormal": "math",
489
+ "\\mathrm": "rm",
490
+ "\\mathit": "it",
491
+ "\\mathbf": "bf",
492
+ "\\mathsf": "sf",
493
+ "\\mathtt": "tt",
494
+ "\\mathfrak": "frak",
495
+ "\\mathcal": "cal",
496
+ "\\mathbb": "bb",
497
+ "\\mathscr": "scr",
498
+ "\\rm": "rm",
499
+ "\\it": "it",
500
+ "\\bf": "bf",
501
+ "\\sf": "tt",
502
+ "\\tt": "tt",
503
+ "\\frak": "frak",
504
+ "\\cal": "cal",
505
+ "\\bb": "bb",
506
+ "\\scr": "scr"
507
+ }[expr.text] ?? mode;
508
+ if (isGrid(expr)) {
509
+ planGrid(expr, tmd);
510
+ return;
511
+ }
512
+ if (!expr.chld.length) {
513
+ if (SYMB[expr.text]) {
514
+ if (SYMB[expr.text].flags.txt) {
515
+ let w = 0;
516
+ const text = SYMB[expr.text].text ?? expr.text.slice(1);
517
+ for (const char of text) {
518
+ w += char === " " ? 8 : HERSHEY(asciiMap(char, "text")).w;
519
+ }
520
+ w /= 16;
521
+ expr.bbox = { x: 0, y: 0, w: expr.text === "\\lim" ? 3.5 : w, h: 2 };
522
+ } else if (SYMB[expr.text].flags.big) {
523
+ expr.bbox = { x: 0, y: -0.5, w: 3, h: 3 };
524
+ } else if (SYMB[expr.text].glyph) {
525
+ let w = HERSHEY(SYMB[expr.text].glyph).w;
526
+ w /= 16;
527
+ if (SYMB[expr.text].flags.integral) {
528
+ expr.bbox = { x: 0, y: -1.5, w, h: 5 };
529
+ } else {
530
+ expr.bbox = { x: 0, y: 0, w, h: 2 };
531
+ }
532
+ } else {
533
+ expr.bbox = { x: 0, y: 0, w: 1, h: 2 };
534
+ }
535
+ } else {
536
+ let w = 0;
537
+ for (let i = 0; i < expr.text.length; i++) {
538
+ const glyph = HERSHEY(asciiMap(expr.text[i], tmd));
539
+ if (!glyph) {
540
+ continue;
541
+ }
542
+ if (tmd === "tt") {
543
+ w += 16;
544
+ } else {
545
+ w += glyph.w;
546
+ }
547
+ }
548
+ w /= 16;
549
+ expr.bbox = { x: 0, y: 0, w, h: 2 };
550
+ }
551
+ expr.mode = tmd;
552
+ return;
553
+ }
554
+ if (SYMB[expr.text]?.flags.group) {
555
+ planGroup(expr, tmd);
556
+ } else if (SYMB[expr.text]?.flags.xarrow) {
557
+ planArrow(expr, tmd);
558
+ } else if (SYMB[expr.text]?.flags.frac) {
559
+ const a = expr.chld[0];
560
+ const b = expr.chld[1];
561
+ const s = expr.text === "\\dfrac" ? 1 : expr.text === "\\tfrac" ? 0.65 : CONFIG.FRAC_SCALE;
562
+ plan(a, tmd);
563
+ plan(b, tmd);
564
+ a.bbox.x = 0;
565
+ a.bbox.y = 0;
566
+ b.bbox.x = 0;
567
+ b.bbox.y = 0;
568
+ const mw = Math.max(a.bbox.w, b.bbox.w) * s;
569
+ transform(a, s, null, (mw - a.bbox.w * s) / 2, 0);
570
+ transform(b, s, null, (mw - b.bbox.w * s) / 2, a.bbox.h + CONFIG.FRAC_SPACING);
571
+ expr.bbox = { x: 0, y: -a.bbox.h + 1 - CONFIG.FRAC_SPACING / 2, w: mw, h: a.bbox.h + b.bbox.h + CONFIG.FRAC_SPACING };
572
+ } else if (SYMB[expr.text]?.flags.binom) {
573
+ const a = expr.chld[0];
574
+ const b = expr.chld[1];
575
+ const s = expr.text === "\\tbinom" ? 0.7 : 1;
576
+ plan(a, tmd);
577
+ plan(b, tmd);
578
+ a.bbox.x = 0;
579
+ a.bbox.y = 0;
580
+ b.bbox.x = 0;
581
+ b.bbox.y = 0;
582
+ const mw = Math.max(a.bbox.w, b.bbox.w) * s;
583
+ transform(a, s, null, (mw - a.bbox.w * s) / 2 + s, 0);
584
+ transform(b, s, null, (mw - b.bbox.w * s) / 2 + s, a.bbox.h);
585
+ expr.bbox = { x: 0, y: -a.bbox.h + 1, w: mw + 2 * s, h: a.bbox.h + b.bbox.h };
586
+ } else if (SYMB[expr.text]?.flags.above || SYMB[expr.text]?.flags.below) {
587
+ const [label, base] = expr.chld;
588
+ plan(label, tmd);
589
+ plan(base, tmd);
590
+ transform(label, CONFIG.SUB_SUP_SCALE, null, 0, 0);
591
+ const above = SYMB[expr.text].flags.above;
592
+ const gap = 0.2;
593
+ const width = Math.max(label.bbox.w, base.bbox.w);
594
+ const baseY = base.bbox.y;
595
+ label.bbox.x = (width - label.bbox.w) / 2;
596
+ base.bbox.x = (width - base.bbox.w) / 2;
597
+ label.bbox.y = above ? 0 : base.bbox.h + gap;
598
+ base.bbox.y = above ? label.bbox.h + gap : 0;
599
+ expr.bbox = { x: 0, y: baseY - base.bbox.y, w: width, h: base.bbox.h + label.bbox.h + gap };
600
+ expr.type = "node";
601
+ } else if (SYMB[expr.text]?.size) {
602
+ const child = expr.chld[0];
603
+ const size = SYMB[expr.text].size;
604
+ plan(child, tmd);
605
+ transform(child, 1, size, 0, 0);
606
+ child.bbox.y = 0;
607
+ expr.bbox = { x: 0, y: 1 - child.bbox.h / 2, w: child.bbox.w, h: child.bbox.h };
608
+ expr.type = "node";
609
+ } else if (expr.text === "\\boxed") {
610
+ const child = expr.chld[0];
611
+ plan(child, tmd);
612
+ const padding = 0.3;
613
+ const childY = child.bbox.y;
614
+ child.bbox.x = padding;
615
+ child.bbox.y = padding;
616
+ expr.bbox = { x: 0, y: childY - padding, w: child.bbox.w + 2 * padding, h: child.bbox.h + 2 * padding };
617
+ } else if (expr.text === "\\sqrt") {
618
+ const e = expr.chld[0];
619
+ plan(e, tmd);
620
+ const f = expr.chld[1];
621
+ let pl = 0;
622
+ if (f) {
623
+ plan(f);
624
+ pl = Math.max(f.bbox.w * CONFIG.SQRT_MAG_SCALE - 0.5, 0);
625
+ transform(f, CONFIG.SQRT_MAG_SCALE, null, 0, 0.5);
626
+ }
627
+ if (SYMB[e.text]?.flags.group || SYMB[e.text]?.flags.xarrow || isGrid(e)) e.bbox.y = 0;
628
+ transform(e, 1, null, 1 + pl, 0.5);
629
+ expr.bbox = { x: 0, y: 2 - e.bbox.h - 0.5, w: e.bbox.w + 1 + pl, h: e.bbox.h + 0.5 };
630
+ } else if (SYMB[expr.text] && SYMB[expr.text].flags.hat) {
631
+ const e = expr.chld[0];
632
+ const localAccent = ["\\dot", "\\ddot", "\\check", "\\mathring", "\\widetilde", "\\overleftrightarrow"].includes(expr.text);
633
+ plan(e, localAccent ? tmd : "math");
634
+ const y0 = e.bbox.y - 0.5;
635
+ e.bbox.y = 0.5;
636
+ expr.bbox = { x: 0, y: y0, w: e.bbox.w, h: e.bbox.h + 0.5 };
637
+ } else if (SYMB[expr.text] && SYMB[expr.text].flags.mat) {
638
+ const e = expr.chld[0];
639
+ plan(e);
640
+ expr.bbox = { x: 0, y: 0, w: e.bbox.w, h: e.bbox.h + 0.5 };
641
+ } else {
642
+ if (expr.text === "\\operatorname" || expr.text === "\\operatorname*") expr.type = "node";
643
+ let dx = 0;
644
+ let dy = 0;
645
+ let mh = 1;
646
+ for (let i = 0; i < expr.chld.length; i++) {
647
+ const c = expr.chld[i];
648
+ const spac = {
649
+ "\\quad": 2,
650
+ "\\qquad": 4,
651
+ "\\enspace": 1,
652
+ "\\enskip": 1,
653
+ "\\,": 2 * 3 / 18,
654
+ "\\thinspace": 2 * 3 / 18,
655
+ "\\:": 2 * 4 / 18,
656
+ "\\medspace": 2 * 4 / 18,
657
+ "\\;": 2 * 5 / 18,
658
+ "\\thickspace": 2 * 5 / 18,
659
+ "\\!": 2 * -3 / 18,
660
+ "\\negthinspace": 2 * -3 / 18
661
+ }[c.text] ?? null;
662
+ if (c.text === "\\\\") {
663
+ dy += mh;
664
+ dx = 0;
665
+ mh = 1;
666
+ continue;
667
+ } else if (c.text === "&") {
668
+ continue;
669
+ } else if (spac !== null && spac !== void 0) {
670
+ dx += spac;
671
+ continue;
672
+ } else {
673
+ if (c.text === "\\dots") {
674
+ const before = expr.chld[i - 1]?.text ?? "";
675
+ const after = expr.chld[i + 1]?.text ?? "";
676
+ const centered = /^(?:[+*=<>-]|\\(?:cdot|times|div|pm|mp|otimes|oplus|odot|ominus|oslash|cup|cap|uplus|land|lor|wedge|vee|leq?|geq?|neq?|approx|sim|simeq|equiv|cong|ll|gg|prec|succ|preceq|succeq|subset|supset|subseteq|supseteq))$/;
677
+ c.text = before === "," || after === "," ? "\\ldots" : centered.test(before) || centered.test(after) ? "\\cdots" : "\\ldots";
678
+ }
679
+ plan(c, tmd);
680
+ transform(c, 1, null, dx, dy);
681
+ if (c.text === "^" || c.text === "_" || c.text === "'") {
682
+ let j = i;
683
+ while (j > 0 && (expr.chld[j].text === "^" || expr.chld[j].text === "_" || expr.chld[j].text === "'")) {
684
+ j--;
685
+ }
686
+ const wasBig = SYMB[expr.chld[j].text] && SYMB[expr.chld[j].text].flags.big;
687
+ if (c.text === "'") {
688
+ let k = j + 1;
689
+ let nth = 0;
690
+ while (k < i) {
691
+ if (expr.chld[k].text === "'") {
692
+ nth++;
693
+ }
694
+ k++;
695
+ }
696
+ c.bbox.x = expr.chld[j].bbox.x + expr.chld[j].bbox.w + c.bbox.w * nth;
697
+ dx = Math.max(dx, c.bbox.x + c.bbox.w);
698
+ } else {
699
+ if (wasBig) {
700
+ const ex = expr.chld[j].bbox.x + (expr.chld[j].bbox.w - c.bbox.w * CONFIG.SUB_SUP_SCALE) / 2;
701
+ c.bbox.x = ex;
702
+ dx = Math.max(dx, expr.chld[j].bbox.x + expr.chld[j].bbox.w + (c.bbox.w * CONFIG.SUB_SUP_SCALE - expr.chld[j].bbox.w) / 2);
703
+ } else {
704
+ c.bbox.x = expr.chld[j].bbox.x + expr.chld[j].bbox.w;
705
+ dx = Math.max(dx, c.bbox.x + c.bbox.w * CONFIG.SUB_SUP_SCALE);
706
+ }
707
+ }
708
+ } else {
709
+ dx += c.bbox.w;
710
+ }
711
+ if (mode === "text") {
712
+ dx += 1;
713
+ }
714
+ mh = Math.max(c.bbox.y + c.bbox.h - dy, mh);
715
+ }
716
+ }
717
+ dy += mh;
718
+ const alt = {
719
+ "bmatrix": "center",
720
+ "pmatrix": "center",
721
+ "Bmatrix": "center",
722
+ "vmatrix": "center",
723
+ "Vmatrix": "center",
724
+ "cases": "left",
725
+ "matrix": "center",
726
+ "aligned": "equation"
727
+ }[expr.text] ?? "left";
728
+ const hasLp = !!delimiters?.length;
729
+ const hasRp = !!delimiters && delimiters.length > 1;
730
+ align(expr.chld, alt);
731
+ const bb = computeBbox(expr.chld);
732
+ if (expr.text === "\\text") {
733
+ bb.x -= 1;
734
+ bb.w += 2;
735
+ }
736
+ for (let i = 0; i < expr.chld.length; i++) {
737
+ transform(expr.chld[i], 1, null, -bb.x + (hasLp ? 1.5 : 0), -bb.y);
738
+ }
739
+ expr.bbox = { x: 0, y: preserveBaseline ? bb.y : 0, w: bb.w + 1.5 * Number(hasLp) + 1.5 * Number(hasRp), h: bb.h };
740
+ if (hasLp) {
741
+ expr.chld.unshift({
742
+ type: "symb",
743
+ text: delimiters[0],
744
+ mode: expr.mode,
745
+ chld: [],
746
+ bbox: { x: 0, y: 0, w: 1, h: bb.h }
747
+ });
748
+ }
749
+ if (hasRp) {
750
+ expr.chld.push({
751
+ type: "symb",
752
+ text: delimiters[1],
753
+ mode: expr.mode,
754
+ chld: [],
755
+ bbox: { x: bb.w + 2, y: 0, w: 1, h: bb.h }
756
+ });
757
+ }
758
+ if (hasLp || hasRp || expr.text === "matrix") {
759
+ expr.type = "node";
760
+ expr.text = "";
761
+ expr.bbox.y -= (expr.bbox.h - 2) / 2;
762
+ }
763
+ }
764
+ }
765
+ function flatten(expr) {
766
+ function flat(expr2, dx, dy) {
767
+ const ff = [];
768
+ if (expr2.bbox) {
769
+ dx += expr2.bbox.x;
770
+ dy += expr2.bbox.y;
771
+ if (SYMB[expr2.text]?.flags.frac) {
772
+ const h = expr2.chld[1].bbox.y - (expr2.chld[0].bbox.y + expr2.chld[0].bbox.h);
773
+ const e = {
774
+ type: "symb",
775
+ mode: expr2.mode,
776
+ text: "\\bar",
777
+ bbox: {
778
+ x: dx,
779
+ y: dy + (expr2.chld[1].bbox.y - h / 2) - h / 2,
780
+ w: expr2.bbox.w,
781
+ h
782
+ },
783
+ chld: []
784
+ };
785
+ ff.push(e);
786
+ } else if (expr2.text === "\\sqrt") {
787
+ const h = expr2.chld[0].bbox.y;
788
+ const xx = Math.max(0, expr2.chld[0].bbox.x - expr2.chld[0].bbox.h / 2);
789
+ const e = {
790
+ type: "symb",
791
+ mode: expr2.mode,
792
+ text: "\\sqrt",
793
+ bbox: {
794
+ x: dx + xx,
795
+ y: dy + h / 2,
796
+ w: expr2.chld[0].bbox.x - xx,
797
+ h: expr2.bbox.h - h / 2
798
+ },
799
+ chld: []
800
+ };
801
+ ff.push(e);
802
+ ff.push({
803
+ type: "symb",
804
+ text: "\\bar",
805
+ mode: expr2.mode,
806
+ bbox: {
807
+ x: dx + expr2.chld[0].bbox.x,
808
+ y: dy,
809
+ w: expr2.bbox.w - expr2.chld[0].bbox.x,
810
+ h
811
+ },
812
+ chld: []
813
+ });
814
+ } else if (SYMB[expr2.text]?.flags.binom) {
815
+ const w = Math.min(expr2.chld[0].bbox.x, expr2.chld[1].bbox.x);
816
+ const e = {
817
+ type: "symb",
818
+ mode: expr2.mode,
819
+ text: "(",
820
+ bbox: {
821
+ x: dx,
822
+ y: dy,
823
+ w,
824
+ h: expr2.bbox.h
825
+ },
826
+ chld: []
827
+ };
828
+ ff.push(e);
829
+ ff.push({
830
+ type: "symb",
831
+ text: ")",
832
+ mode: expr2.mode,
833
+ bbox: {
834
+ x: dx + expr2.bbox.w - w,
835
+ y: dy,
836
+ w,
837
+ h: expr2.bbox.h
838
+ },
839
+ chld: []
840
+ });
841
+ } else if (SYMB[expr2.text] && SYMB[expr2.text].flags.hat) {
842
+ const h = expr2.chld[0].bbox.y;
843
+ const e = {
844
+ type: "symb",
845
+ mode: expr2.mode,
846
+ text: expr2.text,
847
+ bbox: {
848
+ x: dx,
849
+ y: dy,
850
+ w: expr2.bbox.w,
851
+ h
852
+ },
853
+ chld: []
854
+ };
855
+ ff.push(e);
856
+ } else if (SYMB[expr2.text] && SYMB[expr2.text].flags.mat) {
857
+ const h = expr2.chld[0].bbox.h;
858
+ const e = {
859
+ type: "symb",
860
+ text: expr2.text,
861
+ mode: expr2.mode,
862
+ bbox: {
863
+ x: dx,
864
+ y: dy + h,
865
+ w: expr2.bbox.w,
866
+ h: expr2.bbox.h - h
867
+ },
868
+ chld: []
869
+ };
870
+ ff.push(e);
871
+ } else if (expr2.type !== "node" && expr2.text !== "^" && expr2.text !== "_") {
872
+ const e = {
873
+ type: expr2.type === "func" ? "symb" : expr2.type,
874
+ text: expr2.text,
875
+ mode: expr2.mode,
876
+ bbox: {
877
+ x: dx,
878
+ y: dy,
879
+ w: expr2.bbox.w,
880
+ h: expr2.bbox.h
881
+ },
882
+ chld: []
883
+ };
884
+ ff.push(e);
885
+ }
886
+ }
887
+ for (let i = 0; i < expr2.chld.length; i++) {
888
+ const f2 = flat(expr2.chld[i], dx, dy);
889
+ ff.push(...f2);
890
+ }
891
+ return ff;
892
+ }
893
+ const f = flat(expr, -expr.bbox.x, -expr.bbox.y);
894
+ expr.type = "node";
895
+ expr.text = "";
896
+ expr.chld = f;
897
+ }
898
+ function render(expr) {
899
+ const o = [];
900
+ for (let i = 0; i < expr.chld.length; i++) {
901
+ const e = expr.chld[i];
902
+ if (e.type === "stretch") {
903
+ const flags = SYMB[e.text].flags;
904
+ const kind = flags.xarrow ? "arrow" : flags.brace ? "brace" : "paren";
905
+ const lines = stretch(kind, e.bbox.w, e.bbox.h, flags.xarrow ? flags.left : flags.under);
906
+ for (const line of lines) o.push(line.map(([x, y]) => [x + e.bbox.x, y + e.bbox.y]));
907
+ continue;
908
+ }
909
+ let s = e.bbox.h / 2;
910
+ let isSmallHat = false;
911
+ if (SYMB[e.text] && SYMB[e.text].flags.hat && !SYMB[e.text].flags.xfl && !SYMB[e.text].flags.yfl) {
912
+ s *= 4;
913
+ isSmallHat = true;
914
+ }
915
+ if (SYMB[e.text] && SYMB[e.text].glyph) {
916
+ const d = HERSHEY(SYMB[e.text].glyph);
917
+ for (let j = 0; j < d.polylines.length; j++) {
918
+ const l = [];
919
+ for (let k = 0; k < d.polylines[j].length; k++) {
920
+ let x = d.polylines[j][k][0];
921
+ let y = d.polylines[j][k][1];
922
+ if (SYMB[e.text].flags.xfl) {
923
+ x = (x - d.xmin) / Math.max(d.xmax - d.xmin, 1) * e.bbox.w;
924
+ x += e.bbox.x;
925
+ } else if (d.w / 16 * s > e.bbox.w) {
926
+ x = x / Math.max(d.w, 1) * e.bbox.w;
927
+ x += e.bbox.x;
928
+ } else {
929
+ x = x / 16 * s;
930
+ const p = (e.bbox.w - d.w / 16 * s) / 2;
931
+ x += e.bbox.x + p;
932
+ }
933
+ if (SYMB[e.text].flags.yfl) {
934
+ y = (y - d.ymin) / Math.max(d.ymax - d.ymin, 1) * e.bbox.h;
935
+ y += e.bbox.y;
936
+ } else {
937
+ y = y / 16 * s;
938
+ if (isSmallHat) {
939
+ const p = (d.ymax + d.ymin) / 2;
940
+ y -= p / 16 * s;
941
+ }
942
+ y += e.bbox.y + e.bbox.h / 2;
943
+ }
944
+ l.push([x, y]);
945
+ }
946
+ o.push(l);
947
+ }
948
+ } else if (SYMB[e.text] && SYMB[e.text].flags.txt || e.type === "char") {
949
+ let x0 = e.bbox.x;
950
+ const isVerb = !!(SYMB[e.text] && SYMB[e.text].flags.txt);
951
+ const text = isVerb ? SYMB[e.text].text ?? e.text.slice(1) : e.text;
952
+ for (let n = 0; n < text.length; n++) {
953
+ if (isVerb && text[n] === " ") {
954
+ x0 += 0.5 * s;
955
+ continue;
956
+ }
957
+ const d = HERSHEY(asciiMap(text[n], isVerb ? "text" : e.mode));
958
+ if (!d) {
959
+ console.warn("unmapped character: " + text[n]);
960
+ continue;
961
+ }
962
+ for (let j = 0; j < d.polylines.length; j++) {
963
+ const l = [];
964
+ for (let k = 0; k < d.polylines[j].length; k++) {
965
+ let x = d.polylines[j][k][0];
966
+ let y = d.polylines[j][k][1];
967
+ x /= 16;
968
+ y /= 16;
969
+ x *= s;
970
+ y *= s;
971
+ if (e.mode === "tt") {
972
+ if (d.w > 16) {
973
+ x *= 16 / d.w;
974
+ } else {
975
+ x += (16 - d.w) / 2 / 16;
976
+ }
977
+ }
978
+ x += x0;
979
+ y += e.bbox.y + e.bbox.h / 2;
980
+ l.push([x, y]);
981
+ }
982
+ o.push(l);
983
+ }
984
+ if (e.mode === "tt") {
985
+ x0 += s;
986
+ } else {
987
+ x0 += d.w / 16 * s;
988
+ }
989
+ }
990
+ } else {
991
+ }
992
+ }
993
+ return o;
994
+ }
995
+ function nf(x) {
996
+ return Math.round(x * 100) / 100;
997
+ }
998
+ class hfmath {
999
+ _latex;
1000
+ _tree;
1001
+ _tokens;
1002
+ _polylines;
1003
+ constructor(latex) {
1004
+ this._latex = latex;
1005
+ this._tokens = tokenize(latex);
1006
+ const tree = parse(this._tokens);
1007
+ environments(tree.chld);
1008
+ plan(tree);
1009
+ this._tree = tree;
1010
+ flatten(this._tree);
1011
+ this._polylines = render(this._tree);
1012
+ }
1013
+ resolveScale(opt) {
1014
+ if (opt === null || opt === void 0) {
1015
+ return [16, 16, 16, 16];
1016
+ }
1017
+ let sclx = opt.SCALE_X ?? 16;
1018
+ let scly = opt.SCALE_Y ?? 16;
1019
+ if (opt.MIN_CHAR_H !== null && opt.MIN_CHAR_H !== void 0) {
1020
+ let mh = Infinity;
1021
+ for (let i = 0; i < this._tree.chld.length; i++) {
1022
+ const c = this._tree.chld[i];
1023
+ if (c.type === "char" || SYMB[c.text] && (SYMB[c.text].flags.txt || !Object.keys(SYMB[c.text].flags).length)) {
1024
+ if (c.bbox.h > 0) mh = Math.min(c.bbox.h, mh);
1025
+ }
1026
+ }
1027
+ if (Number.isFinite(mh) && scly !== 0) {
1028
+ const s = Math.max(1, opt.MIN_CHAR_H / (mh * Math.abs(scly)));
1029
+ sclx *= s;
1030
+ scly *= s;
1031
+ }
1032
+ }
1033
+ if (opt.MAX_W !== null && opt.MAX_W !== void 0 && this._tree.bbox.w > 0 && sclx !== 0) {
1034
+ const s0 = sclx;
1035
+ sclx = Math.min(sclx, opt.MAX_W / this._tree.bbox.w);
1036
+ scly *= sclx / s0;
1037
+ }
1038
+ if (opt.MAX_H !== null && opt.MAX_H !== void 0 && this._tree.bbox.h > 0 && scly !== 0) {
1039
+ const s0 = scly;
1040
+ scly = Math.min(scly, opt.MAX_H / this._tree.bbox.h);
1041
+ sclx *= scly / s0;
1042
+ }
1043
+ const px = opt.MARGIN_X ?? sclx;
1044
+ const py = opt.MARGIN_Y ?? scly;
1045
+ return [px, py, sclx, scly];
1046
+ }
1047
+ polylines(opt) {
1048
+ if (!opt) opt = {};
1049
+ const polylines = [];
1050
+ const [px, py, sclx, scly] = this.resolveScale(opt);
1051
+ for (let i = 0; i < this._polylines.length; i++) {
1052
+ polylines.push([]);
1053
+ for (let j = 0; j < this._polylines[i].length; j++) {
1054
+ const [x, y] = this._polylines[i][j];
1055
+ polylines[polylines.length - 1].push([px + x * sclx, py + y * scly]);
1056
+ }
1057
+ }
1058
+ return polylines;
1059
+ }
1060
+ pathd(opt) {
1061
+ if (!opt) opt = {};
1062
+ let d = "";
1063
+ const [px, py, sclx, scly] = this.resolveScale(opt);
1064
+ for (let i = 0; i < this._polylines.length; i++) {
1065
+ for (let j = 0; j < this._polylines[i].length; j++) {
1066
+ const [x, y] = this._polylines[i][j];
1067
+ d += !j ? "M" : "L";
1068
+ d += `${nf(px + x * sclx)} ${nf(py + y * scly)}`;
1069
+ }
1070
+ }
1071
+ return d;
1072
+ }
1073
+ svg(opt) {
1074
+ if (!opt) opt = {};
1075
+ const [px, py, sclx, scly] = this.resolveScale(opt);
1076
+ const w = nf(this._tree.bbox.w * sclx + px * 2);
1077
+ const h = nf(this._tree.bbox.h * scly + py * 2);
1078
+ let o = `<svg
1079
+ xmlns="http://www.w3.org/2000/svg"
1080
+ width="${w}" height="${h}"
1081
+ fill="none" stroke="${opt.FG_COLOR ?? "black"}" stroke-width="${opt.STROKE_W ?? 1}"
1082
+ stroke-linecap="round" stroke-linejoin="round"
1083
+ >`;
1084
+ if (opt.BG_COLOR) {
1085
+ o += `<rect x="${0}" y="${0}" width="${w}" height="${h}" fill="${opt.BG_COLOR}" stroke="none"></rect>`;
1086
+ }
1087
+ o += `<path d="`;
1088
+ for (let i = 0; i < this._polylines.length; i++) {
1089
+ o += "M";
1090
+ for (let j = 0; j < this._polylines[i].length; j++) {
1091
+ const [x, y] = this._polylines[i][j];
1092
+ o += nf(px + x * sclx) + " " + nf(py + y * scly) + " ";
1093
+ }
1094
+ }
1095
+ o += `"/>`;
1096
+ o += `</svg>`;
1097
+ return o;
1098
+ }
1099
+ pdf(opt) {
1100
+ if (!opt) opt = {};
1101
+ const [px, py, sclx, scly] = this.resolveScale(opt);
1102
+ const width = nf(this._tree.bbox.w * sclx + px * 2);
1103
+ const height = nf(this._tree.bbox.h * scly + py * 2);
1104
+ let head = `%PDF-1.1
1105
+ %%\xA5\xB1\xEB
1106
+ 1 0 obj
1107
+ << /Type /Catalog
1108
+ /Pages 2 0 R
1109
+ >>endobj
1110
+ 2 0 obj
1111
+ << /Type /Pages
1112
+ /Kids [3 0 R]
1113
+ /Count 1
1114
+ /MediaBox [0 0 ${width} ${height}]
1115
+ >>
1116
+ endobj
1117
+ 3 0 obj
1118
+ << /Type /Page
1119
+ /Parent 2 0 R
1120
+ /Resources
1121
+ << /Font
1122
+ << /F1
1123
+ << /Type /Font
1124
+ /Subtype /Type1
1125
+ /BaseFont /Times-Roman
1126
+ >>
1127
+ >>
1128
+ >>
1129
+ /Contents [`;
1130
+ let pdf = "";
1131
+ let count = 4;
1132
+ for (let i = 0; i < this._polylines.length; i++) {
1133
+ pdf += `${count} 0 obj
1134
+ << /Length 0 >>
1135
+ stream
1136
+ 1 j 1 J ${opt.STROKE_W ?? 1} w
1137
+ `;
1138
+ for (let j = 0; j < this._polylines[i].length; j++) {
1139
+ const [x, y] = this._polylines[i][j];
1140
+ pdf += `${nf(px + x * sclx)} ${nf(height - (py + y * scly))} ${j ? "l" : "m"} `;
1141
+ }
1142
+ pdf += "\nS\nendstream\nendobj\n";
1143
+ head += `${count} 0 R `;
1144
+ count++;
1145
+ }
1146
+ head += "]\n>>\nendobj\n";
1147
+ pdf += "\ntrailer\n<< /Root 1 0 R \n /Size 0\n >>startxref\n\n%%EOF\n";
1148
+ return head + pdf;
1149
+ }
1150
+ boxes(opt) {
1151
+ if (!opt) opt = {};
1152
+ const [px, py, sclx, scly] = this.resolveScale(opt);
1153
+ const bs = [];
1154
+ for (let i = 0; i < this._tree.chld.length; i++) {
1155
+ const { x, y, w, h } = this._tree.chld[i].bbox;
1156
+ bs.push({ x: px + x * sclx, y: py + y * scly, w: w * sclx, h: h * scly });
1157
+ }
1158
+ return bs;
1159
+ }
1160
+ box(opt) {
1161
+ if (!opt) opt = {};
1162
+ const [px, py, sclx, scly] = this.resolveScale(opt);
1163
+ return {
1164
+ x: px + this._tree.bbox.x * sclx,
1165
+ y: py + this._tree.bbox.y * scly,
1166
+ w: this._tree.bbox.w * sclx,
1167
+ h: this._tree.bbox.h * scly
1168
+ };
1169
+ }
1170
+ }
1171
+ const _impl = { tokenize, parse, environments, plan, flatten, render };
1172
+ export {
1173
+ CONFIG,
1174
+ _impl,
1175
+ hfmath
1176
+ };