@lofcz/tikz 0.0.0-stage → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (118) hide show
  1. package/LICENSE +674 -0
  2. package/NOTICE.md +30 -0
  3. package/README.md +127 -2
  4. package/package.json +56 -4
  5. package/runtime/LICENSE +165 -0
  6. package/runtime/NOTICE.md +32 -0
  7. package/runtime/dist/auto.d.ts +68 -0
  8. package/runtime/dist/auto.js +564 -0
  9. package/runtime/dist/bibtex.mjs +2 -0
  10. package/runtime/dist/bibtex.wasm +0 -0
  11. package/runtime/dist/bundles/cm-tfm/files.br +0 -0
  12. package/runtime/dist/bundles/cm-tfm/files.index.json +1 -0
  13. package/runtime/dist/bundles/cm-tfm/manifest.json +1 -0
  14. package/runtime/dist/bundles/cm-type1/files.br +0 -0
  15. package/runtime/dist/bundles/cm-type1/files.index.json +1 -0
  16. package/runtime/dist/bundles/cm-type1/manifest.json +1 -0
  17. package/runtime/dist/bundles/core/files.br +0 -0
  18. package/runtime/dist/bundles/core/files.index.json +1 -0
  19. package/runtime/dist/bundles/core/manifest.json +1 -0
  20. package/runtime/dist/bundles/hot.json +1 -0
  21. package/runtime/dist/bundles/index.json +80 -0
  22. package/runtime/dist/bundles/latex-core/files.br +0 -0
  23. package/runtime/dist/bundles/latex-core/files.index.json +1 -0
  24. package/runtime/dist/bundles/latex-core/manifest.json +1 -0
  25. package/runtime/dist/bundles/latex-extra/files.br +0 -0
  26. package/runtime/dist/bundles/latex-extra/files.index.json +1 -0
  27. package/runtime/dist/bundles/latex-extra/manifest.json +1 -0
  28. package/runtime/dist/bundles/lm-fonts/files.br +0 -0
  29. package/runtime/dist/bundles/lm-fonts/files.index.json +1 -0
  30. package/runtime/dist/bundles/lm-fonts/manifest.json +1 -0
  31. package/runtime/dist/bundles/luatex/files.br +0 -0
  32. package/runtime/dist/bundles/luatex/files.index.json +1 -0
  33. package/runtime/dist/bundles/luatex/manifest.json +1 -0
  34. package/runtime/dist/bundles/ps-fonts/files.br +0 -0
  35. package/runtime/dist/bundles/ps-fonts/files.index.json +1 -0
  36. package/runtime/dist/bundles/ps-fonts/manifest.json +1 -0
  37. package/runtime/dist/bundles/tex-plain/files.br +0 -0
  38. package/runtime/dist/bundles/tex-plain/files.index.json +1 -0
  39. package/runtime/dist/bundles/tex-plain/manifest.json +1 -0
  40. package/runtime/dist/bundles/tikz-snapshot/files.br +0 -0
  41. package/runtime/dist/bundles/tikz-snapshot/files.index.json +1 -0
  42. package/runtime/dist/bundles/tikz-snapshot/manifest.json +1 -0
  43. package/runtime/dist/bundles-config.d.ts +3 -0
  44. package/runtime/dist/bundles-config.js +16 -0
  45. package/runtime/dist/cli.d.ts +2 -0
  46. package/runtime/dist/cli.js +372 -0
  47. package/runtime/dist/core.d.ts +124 -0
  48. package/runtime/dist/core.js +927 -0
  49. package/runtime/dist/diagnostics.d.ts +53 -0
  50. package/runtime/dist/diagnostics.js +184 -0
  51. package/runtime/dist/dvisvgm.d.ts +20 -0
  52. package/runtime/dist/dvisvgm.js +46 -0
  53. package/runtime/dist/dvisvgm.mjs +2 -0
  54. package/runtime/dist/dvisvgm.wasm +0 -0
  55. package/runtime/dist/figures.d.ts +92 -0
  56. package/runtime/dist/figures.js +250 -0
  57. package/runtime/dist/ghostscript.d.ts +92 -0
  58. package/runtime/dist/ghostscript.js +218 -0
  59. package/runtime/dist/index.d.ts +87 -0
  60. package/runtime/dist/index.js +364 -0
  61. package/runtime/dist/logger.d.ts +48 -0
  62. package/runtime/dist/logger.js +55 -0
  63. package/runtime/dist/luatex.mjs +2 -0
  64. package/runtime/dist/luatex.wasm +0 -0
  65. package/runtime/dist/makeindex.mjs +2 -0
  66. package/runtime/dist/makeindex.wasm +0 -0
  67. package/runtime/dist/mplib.d.ts +106 -0
  68. package/runtime/dist/mplib.js +85 -0
  69. package/runtime/dist/mplib.mjs +2 -0
  70. package/runtime/dist/mplib.wasm +0 -0
  71. package/runtime/dist/node.d.ts +4 -0
  72. package/runtime/dist/node.js +49 -0
  73. package/runtime/dist/postscript.d.ts +21 -0
  74. package/runtime/dist/postscript.js +130 -0
  75. package/runtime/dist/prerender.d.ts +55 -0
  76. package/runtime/dist/prerender.js +106 -0
  77. package/runtime/dist/render/svg.d.ts +20 -0
  78. package/runtime/dist/render/svg.js +96 -0
  79. package/runtime/dist/tex/bridge.d.ts +93 -0
  80. package/runtime/dist/tex/bridge.js +223 -0
  81. package/runtime/dist/tex/cache-key.d.ts +33 -0
  82. package/runtime/dist/tex/cache-key.js +150 -0
  83. package/runtime/dist/tex/mpto.d.ts +60 -0
  84. package/runtime/dist/tex/mpto.js +112 -0
  85. package/runtime/dist/tex/mpx.d.ts +25 -0
  86. package/runtime/dist/tex/mpx.js +54 -0
  87. package/runtime/dist/tex/scanner.d.ts +78 -0
  88. package/runtime/dist/tex/scanner.js +326 -0
  89. package/runtime/dist/tex.mjs +2 -0
  90. package/runtime/dist/tex.wasm +0 -0
  91. package/runtime/dist/texengine.d.ts +35 -0
  92. package/runtime/dist/texengine.js +57 -0
  93. package/runtime/dist/texutils.d.ts +19 -0
  94. package/runtime/dist/texutils.js +60 -0
  95. package/runtime/dist/types.d.ts +376 -0
  96. package/runtime/dist/types.js +6 -0
  97. package/runtime/dist/vfs/bundle.d.ts +88 -0
  98. package/runtime/dist/vfs/bundle.js +315 -0
  99. package/runtime/dist/vfs/lazyfs.d.ts +62 -0
  100. package/runtime/dist/vfs/lazyfs.js +133 -0
  101. package/runtime/dist/worker.d.ts +15 -0
  102. package/runtime/dist/worker.js +143 -0
  103. package/runtime/licenses/COPYING.AFPL +230 -0
  104. package/runtime/licenses/COPYING.makeindex +42 -0
  105. package/runtime/licenses/GPL-3.0.txt +674 -0
  106. package/runtime/licenses/LGPL-3.0.txt +165 -0
  107. package/runtime/licenses/OFL-1.1.txt +93 -0
  108. package/runtime/package.json +1 -0
  109. package/runtime/upstream.json +27 -0
  110. package/src/browser-worker.js +26 -0
  111. package/src/browser.d.ts +18 -0
  112. package/src/browser.js +68 -0
  113. package/src/deploy.d.ts +5 -0
  114. package/src/deploy.js +48 -0
  115. package/src/index.d.ts +28 -0
  116. package/src/index.js +103 -0
  117. package/src/worker.js +15 -0
  118. package/upstream.json +27 -0
@@ -0,0 +1,150 @@
1
+ /**
2
+ * cache-key.ts — content-addressed keys for the two TeX-bridge caches
3
+ * (docs/05 §6): L2 snippet keys and L1 document keys.
4
+ *
5
+ * Both are SHA-256 over a canonical, length-prefixed encoding, so no two
6
+ * distinct inputs can collide by concatenation ("ab"+"c" vs "a"+"bc"), and a
7
+ * snippet key can never equal a document key (each kind carries its own tag).
8
+ *
9
+ * The digest is computed by a small pure-TypeScript SHA-256 rather than
10
+ * `crypto.subtle.digest`, because the `make_text` hook that consults the
11
+ * snippet cache must answer synchronously and Web Crypto is Promise-only.
12
+ * Dependency-free and browser-safe.
13
+ */
14
+ /* ---- canonical encoding --------------------------------------------------- */
15
+ class Encoder {
16
+ parts = [];
17
+ total = 0;
18
+ static utf8 = new TextEncoder();
19
+ push(bytes) {
20
+ this.parts.push(bytes);
21
+ this.total += bytes.length;
22
+ }
23
+ u32(n) {
24
+ if (!Number.isInteger(n) || n < 0 || n > 0xffffffff)
25
+ throw new RangeError(`u32 out of range: ${n}`);
26
+ this.push(new Uint8Array([(n >>> 24) & 0xff, (n >>> 16) & 0xff, (n >>> 8) & 0xff, n & 0xff]));
27
+ return this;
28
+ }
29
+ /** A string as `u32 byteLength` + UTF-8 bytes. */
30
+ str(s) {
31
+ const b = Encoder.utf8.encode(s);
32
+ this.u32(b.length);
33
+ this.push(b);
34
+ return this;
35
+ }
36
+ /** A list of strings as `u32 count` + each string. */
37
+ list(items) {
38
+ this.u32(items.length);
39
+ for (const s of items)
40
+ this.str(s);
41
+ return this;
42
+ }
43
+ bytes() {
44
+ const out = new Uint8Array(this.total);
45
+ let o = 0;
46
+ for (const p of this.parts) {
47
+ out.set(p, o);
48
+ o += p.length;
49
+ }
50
+ return out;
51
+ }
52
+ }
53
+ /** Version the encodings so a change in layout can never alias an old key. */
54
+ const SNIPPET_TAG = 'mp-tikz-wasm:snippet:1';
55
+ const DOCUMENT_TAG = 'mp-tikz-wasm:document:1';
56
+ /** L2 key: H(engineId, formatId, verbatimtex chain, body, texscriptmode). */
57
+ export function snippetKey(parts) {
58
+ const mode = parts.texscriptmode ?? 1;
59
+ const enc = new Encoder()
60
+ .str(SNIPPET_TAG)
61
+ .str(parts.engineId)
62
+ .str(parts.formatId)
63
+ .list(parts.chain)
64
+ .str(parts.body)
65
+ .str(String(mode));
66
+ return sha256Hex(enc.bytes());
67
+ }
68
+ /** L1 key: H(full generated .tex, engineId, formatId). */
69
+ export function documentKey(texSource, engineId, formatId) {
70
+ const enc = new Encoder().str(DOCUMENT_TAG).str(engineId).str(formatId).str(texSource);
71
+ return sha256Hex(enc.bytes());
72
+ }
73
+ /* ---- SHA-256 (FIPS 180-4), synchronous ------------------------------------ */
74
+ const K = new Uint32Array([
75
+ 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
76
+ 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
77
+ 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
78
+ 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
79
+ 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
80
+ 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
81
+ 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
82
+ 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2,
83
+ ]);
84
+ /** SHA-256 of `data` (a string is UTF-8 encoded first) as lowercase hex. */
85
+ export function sha256Hex(data) {
86
+ const d = sha256(typeof data === 'string' ? new TextEncoder().encode(data) : data);
87
+ let hex = '';
88
+ for (let i = 0; i < d.length; i++)
89
+ hex += (d[i] < 16 ? '0' : '') + d[i].toString(16);
90
+ return hex;
91
+ }
92
+ /** SHA-256 of `msg`, as 32 bytes. */
93
+ export function sha256(msg) {
94
+ const H = new Uint32Array([
95
+ 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19,
96
+ ]);
97
+ const W = new Uint32Array(64);
98
+ // Padding: 0x80, zeros, then the 64-bit big-endian bit length.
99
+ const len = msg.length;
100
+ const padded = new Uint8Array(((len + 9 + 63) >> 6) << 6);
101
+ padded.set(msg);
102
+ padded[len] = 0x80;
103
+ const bitsHi = Math.floor(len / 0x20000000); // len * 8 / 2^32
104
+ const bitsLo = (len << 3) >>> 0;
105
+ const view = new DataView(padded.buffer);
106
+ view.setUint32(padded.length - 8, bitsHi);
107
+ view.setUint32(padded.length - 4, bitsLo);
108
+ for (let off = 0; off < padded.length; off += 64) {
109
+ for (let t = 0; t < 16; t++)
110
+ W[t] = view.getUint32(off + t * 4);
111
+ for (let t = 16; t < 64; t++) {
112
+ const w15 = W[t - 15];
113
+ const w2 = W[t - 2];
114
+ const s0 = ((w15 >>> 7) | (w15 << 25)) ^ ((w15 >>> 18) | (w15 << 14)) ^ (w15 >>> 3);
115
+ const s1 = ((w2 >>> 17) | (w2 << 15)) ^ ((w2 >>> 19) | (w2 << 13)) ^ (w2 >>> 10);
116
+ W[t] = (W[t - 16] + s0 + W[t - 7] + s1) >>> 0;
117
+ }
118
+ let a = H[0], b = H[1], c = H[2], d = H[3], e = H[4], f = H[5], g = H[6], h = H[7];
119
+ for (let t = 0; t < 64; t++) {
120
+ const S1 = ((e >>> 6) | (e << 26)) ^ ((e >>> 11) | (e << 21)) ^ ((e >>> 25) | (e << 7));
121
+ const ch = (e & f) ^ (~e & g);
122
+ const T1 = (h + S1 + ch + K[t] + W[t]) >>> 0;
123
+ const S0 = ((a >>> 2) | (a << 30)) ^ ((a >>> 13) | (a << 19)) ^ ((a >>> 22) | (a << 10));
124
+ const maj = (a & b) ^ (a & c) ^ (b & c);
125
+ const T2 = (S0 + maj) >>> 0;
126
+ h = g;
127
+ g = f;
128
+ f = e;
129
+ e = (d + T1) >>> 0;
130
+ d = c;
131
+ c = b;
132
+ b = a;
133
+ a = (T1 + T2) >>> 0;
134
+ }
135
+ H[0] = (H[0] + a) >>> 0;
136
+ H[1] = (H[1] + b) >>> 0;
137
+ H[2] = (H[2] + c) >>> 0;
138
+ H[3] = (H[3] + d) >>> 0;
139
+ H[4] = (H[4] + e) >>> 0;
140
+ H[5] = (H[5] + f) >>> 0;
141
+ H[6] = (H[6] + g) >>> 0;
142
+ H[7] = (H[7] + h) >>> 0;
143
+ }
144
+ const out = new Uint8Array(32);
145
+ const ov = new DataView(out.buffer);
146
+ for (let i = 0; i < 8; i++)
147
+ ov.setUint32(i * 4, H[i]);
148
+ return out;
149
+ }
150
+ //# sourceMappingURL=cache-key.js.map
@@ -0,0 +1,60 @@
1
+ /**
2
+ * mpto.ts — builds the batched TeX job from scanned blocks (docs/05 §3.1, §4).
3
+ *
4
+ * `buildTexJob` reproduces byte for byte the file that upstream `mpx_mpto`
5
+ * (texk/web2c/mplibdir/mpxout.w) writes for the same input. The string
6
+ * tables below are copied verbatim from mpxout.w — including their printf
7
+ * escapes, which `cfmt` interprets — so a future upstream change can be
8
+ * diffed against them directly.
9
+ */
10
+ import type { TexBlock } from './scanner.js';
11
+ export type MptoMode = 'tex' | 'troff';
12
+ /**
13
+ * How the C library rendered the malformed conversion `%\n` that upstream's
14
+ * `mpx_pretex1` contains twice (`\bgroup%\n` and `\bgroup}%\n` — a printf
15
+ * format with `%` followed by a newline). The C standard leaves it undefined
16
+ * and libcs differ, so `mpost` binaries differ too:
17
+ *
18
+ * - `glibc` (Linux): `printf_unknown` prints the spec verbatim, so the `%`
19
+ * survives as the TeX comment the author intended. The default.
20
+ * - `bsd` (Apple libc, FreeBSD): "pretend it was %c with argument ch", so
21
+ * the `%` vanishes and only the newline is printed.
22
+ * - musl (Emscripten's libc) rejects the format and prints *nothing* for
23
+ * that fprintf — the whole prologue and the first `\mpxshipout% line`
24
+ * are missing — which is why the wasm build must not call the C mpto.
25
+ *
26
+ * TeX skips the end-of-line space after the control word `\bgroup`, so the
27
+ * DVI and the .mpx are identical for both renderings.
28
+ */
29
+ export type PrintfLibc = 'glibc' | 'bsd';
30
+ export interface BuildTexJobOptions {
31
+ /** TeX (default) or troff output. Index 0 / 1 of the string tables. */
32
+ mode?: MptoMode;
33
+ /** Contents of the MPTEXPRE file (docs/05 §9); prepended as-is in TeX mode
34
+ * only, exactly as upstream copies the file — no newline is added. */
35
+ mptexpre?: string;
36
+ /** Which libc's rendering of the `%\n` quirk to reproduce; default 'glibc'. */
37
+ libc?: PrintfLibc;
38
+ }
39
+ /**
40
+ * mpto's "put no % at end if it's only 1 line total, starting with %" rule
41
+ * (the `%&format` special case): after trimming, a btex body gets a `%`
42
+ * appended unless it is a single line that begins with `%`. An empty body
43
+ * gets one too.
44
+ */
45
+ export declare function needsPercent(body: string): boolean;
46
+ /**
47
+ * Emit the TeX (or troff) job for `blocks`, in source order. Blocks may come
48
+ * from several files (their `file` and `line` are used for the
49
+ * `% line N file` wrapper comments); the "first btex" / "first verbatimtex"
50
+ * bookkeeping is per job, as it is per mpto run upstream.
51
+ */
52
+ export declare function buildTexJob(blocks: readonly TexBlock[], options?: BuildTexJobOptions): string;
53
+ /**
54
+ * DVI page `pageIndex` (0-based) was produced by the `pageIndex`-th `btex`
55
+ * block of the job: every btex block ships out exactly one page, in source
56
+ * order, and verbatimtex blocks ship nothing (docs/05 §3.1). Blocks that
57
+ * mpto flagged with an error still get a (blank) page. Returns undefined
58
+ * when the DVI has more pages than the job has btex blocks.
59
+ */
60
+ export declare function blockForPage(blocks: readonly TexBlock[], pageIndex: number): TexBlock | undefined;
@@ -0,0 +1,112 @@
1
+ /* ---- static strings for mpto, verbatim from mpxout.w -------------------- */
2
+ const mpx_predoc = ['', '.po 0\n'];
3
+ const mpx_postdoc = ['\\end{document}\n', ''];
4
+ const mpx_pretex1 = [
5
+ '\\gdef\\mpxshipout{\\shipout\\hbox\\bgroup%\n' +
6
+ ' \\setbox0=\\hbox\\bgroup}%\n' +
7
+ '\\gdef\\stopmpxshipout{\\egroup' +
8
+ ' \\dimen0=\\ht0 \\advance\\dimen0\\dp0\n' +
9
+ ' \\dimen1=\\ht0 \\dimen2=\\dp0\n' +
10
+ ' \\setbox0=\\hbox\\bgroup\n' +
11
+ ' \\box0\n' +
12
+ ' \\ifnum\\dimen0>0 \\vrule width1sp height\\dimen1 depth\\dimen2 \n' +
13
+ ' \\else \\vrule width1sp height1sp depth0sp\\relax\n' +
14
+ ' \\fi\\egroup\n' +
15
+ ' \\ht0=0pt \\dp0=0pt \\box0 \\egroup}\n' +
16
+ '\\mpxshipout%% line %d %s\n',
17
+ '.lf %d %s\n',
18
+ ];
19
+ const mpx_pretex = ['\\mpxshipout%% line %d %s\n', '.bp\n.lf %d %s\n'];
20
+ const mpx_posttex = ['\n\\stopmpxshipout\n', '\n'];
21
+ const mpx_preverb1 = ['', '.lf %d %s\n']; /* if very first instance */
22
+ const mpx_preverb = ['%% line %d %s\n', '.lf %d %s\n']; /* all other instances */
23
+ const mpx_postverb = ['\n', '\n'];
24
+ /** The subset of printf that the tables use: `%%`, `%d`, `%s` — plus the
25
+ * malformed `%\n`, rendered the way the chosen libc renders it. */
26
+ function cfmt(fmt, line, file, libc) {
27
+ let out = '';
28
+ for (let i = 0; i < fmt.length; i++) {
29
+ const c = fmt[i];
30
+ if (c !== '%') {
31
+ out += c;
32
+ continue;
33
+ }
34
+ const d = fmt[++i];
35
+ if (d === '%')
36
+ out += '%';
37
+ else if (d === 'd')
38
+ out += String(line);
39
+ else if (d === 's')
40
+ out += file;
41
+ else if (d === undefined)
42
+ throw new Error('cfmt: format ends with %');
43
+ else
44
+ out += libc === 'bsd' ? d : '%' + d; // unknown conversion (see PrintfLibc)
45
+ }
46
+ return out;
47
+ }
48
+ /**
49
+ * mpto's "put no % at end if it's only 1 line total, starting with %" rule
50
+ * (the `%&format` special case): after trimming, a btex body gets a `%`
51
+ * appended unless it is a single line that begins with `%`. An empty body
52
+ * gets one too.
53
+ */
54
+ export function needsPercent(body) {
55
+ return body.length === 0 || body.includes('\n') || body[0] !== '%';
56
+ }
57
+ /**
58
+ * Emit the TeX (or troff) job for `blocks`, in source order. Blocks may come
59
+ * from several files (their `file` and `line` are used for the
60
+ * `% line N file` wrapper comments); the "first btex" / "first verbatimtex"
61
+ * bookkeeping is per job, as it is per mpto run upstream.
62
+ */
63
+ export function buildTexJob(blocks, options = {}) {
64
+ const m = options.mode === 'troff' ? 1 : 0;
65
+ const libc = options.libc ?? 'glibc';
66
+ let out = '';
67
+ if (m === 0 && options.mptexpre !== undefined)
68
+ out += options.mptexpre;
69
+ out += mpx_predoc[m];
70
+ let texcnt = 0;
71
+ let verbcnt = 0;
72
+ for (const b of blocks) {
73
+ if (b.kind === 'btex') {
74
+ out += cfmt(texcnt++ === 0 ? mpx_pretex1[m] : mpx_pretex[m], b.line, b.file, libc);
75
+ if (b.error === undefined) {
76
+ out += b.body;
77
+ if (m === 0 && needsPercent(b.body))
78
+ out += '%';
79
+ }
80
+ out += mpx_posttex[m];
81
+ }
82
+ else {
83
+ out += cfmt(verbcnt++ === 0 && texcnt === 0 ? mpx_preverb1[m] : mpx_preverb[m], b.line, b.file, libc);
84
+ if (b.error === undefined)
85
+ out += b.body;
86
+ out += mpx_postverb[m];
87
+ }
88
+ }
89
+ out += mpx_postdoc[m];
90
+ return out;
91
+ }
92
+ /**
93
+ * DVI page `pageIndex` (0-based) was produced by the `pageIndex`-th `btex`
94
+ * block of the job: every btex block ships out exactly one page, in source
95
+ * order, and verbatimtex blocks ship nothing (docs/05 §3.1). Blocks that
96
+ * mpto flagged with an error still get a (blank) page. Returns undefined
97
+ * when the DVI has more pages than the job has btex blocks.
98
+ */
99
+ export function blockForPage(blocks, pageIndex) {
100
+ if (pageIndex < 0 || !Number.isInteger(pageIndex))
101
+ return undefined;
102
+ let k = 0;
103
+ for (const b of blocks) {
104
+ if (b.kind !== 'btex')
105
+ continue;
106
+ if (k === pageIndex)
107
+ return b;
108
+ k++;
109
+ }
110
+ return undefined;
111
+ }
112
+ //# sourceMappingURL=mpto.js.map
@@ -0,0 +1,25 @@
1
+ /**
2
+ * mpx.ts — splits a `.mpx` file (dvitomp output) into one MetaPost primary
3
+ * per typeset block (docs/05 §4.1).
4
+ *
5
+ * The format, as written by `mpx_run_dvitomp`:
6
+ *
7
+ * % Written by metapost version 2.11 <- banner
8
+ * begingroup save _p,_r,_s,_n; ... <- chunk 0
9
+ * _p endgroup
10
+ * mpxbreak
11
+ * begingroup ... <- chunk 1
12
+ * _p endgroup
13
+ * mpxbreak
14
+ *
15
+ * Chunk k corresponds to btex block k; each chunk is self-contained and is a
16
+ * valid MetaPost primary, so it can be returned from `make_text` verbatim.
17
+ */
18
+ /**
19
+ * Drop the leading `%` banner, split on lines equal to `mpxbreak`, and return
20
+ * the chunks. Leading and trailing empty lines of a chunk are removed (so a
21
+ * chunk ends with `_p endgroup`, not a newline); a trailing chunk that is
22
+ * empty or whitespace-only — the normal remainder after the final `mpxbreak`
23
+ * — is ignored. CRLF line ends are accepted.
24
+ */
25
+ export declare function splitMpx(mpx: string): string[];
@@ -0,0 +1,54 @@
1
+ /**
2
+ * mpx.ts — splits a `.mpx` file (dvitomp output) into one MetaPost primary
3
+ * per typeset block (docs/05 §4.1).
4
+ *
5
+ * The format, as written by `mpx_run_dvitomp`:
6
+ *
7
+ * % Written by metapost version 2.11 <- banner
8
+ * begingroup save _p,_r,_s,_n; ... <- chunk 0
9
+ * _p endgroup
10
+ * mpxbreak
11
+ * begingroup ... <- chunk 1
12
+ * _p endgroup
13
+ * mpxbreak
14
+ *
15
+ * Chunk k corresponds to btex block k; each chunk is self-contained and is a
16
+ * valid MetaPost primary, so it can be returned from `make_text` verbatim.
17
+ */
18
+ /**
19
+ * Drop the leading `%` banner, split on lines equal to `mpxbreak`, and return
20
+ * the chunks. Leading and trailing empty lines of a chunk are removed (so a
21
+ * chunk ends with `_p endgroup`, not a newline); a trailing chunk that is
22
+ * empty or whitespace-only — the normal remainder after the final `mpxbreak`
23
+ * — is ignored. CRLF line ends are accepted.
24
+ */
25
+ export function splitMpx(mpx) {
26
+ const lines = mpx.split('\n').map((l) => (l.endsWith('\r') ? l.slice(0, -1) : l));
27
+ if (lines.length > 0 && lines[lines.length - 1] === '')
28
+ lines.pop(); // trailing newline
29
+ if (lines.length > 0 && lines[0].startsWith('%'))
30
+ lines.shift(); // banner
31
+ const chunks = [];
32
+ let cur = [];
33
+ const flush = (last) => {
34
+ while (cur.length > 0 && cur[0] === '')
35
+ cur.shift();
36
+ while (cur.length > 0 && cur[cur.length - 1] === '')
37
+ cur.pop();
38
+ if (last && cur.every((l) => l.trim() === '')) {
39
+ cur = [];
40
+ return;
41
+ }
42
+ chunks.push(cur.join('\n'));
43
+ cur = [];
44
+ };
45
+ for (const line of lines) {
46
+ if (line === 'mpxbreak')
47
+ flush(false);
48
+ else
49
+ cur.push(line);
50
+ }
51
+ flush(true);
52
+ return chunks;
53
+ }
54
+ //# sourceMappingURL=mpx.js.map
@@ -0,0 +1,78 @@
1
+ /**
2
+ * scanner.ts — a faithful TypeScript port of upstream mpto's lexer
3
+ * (`mpx_getline`, `mpx_match_str`, `mpx_getbta`, `mpx_copy_mpto` and the
4
+ * "Do a line" loop of `mpx_mpto` in texk/web2c/mplibdir/mpxout.w).
5
+ *
6
+ * The port exists so that the TeX bridge (docs/05 §3) can find `btex` /
7
+ * `verbatimtex` blocks without writing temporaries, and so that
8
+ * `buildTexJob(scanTexBlocks(src))` (src/ts/tex/mpto.ts) reproduces the .tex
9
+ * file that C `mpx_mpto` writes byte for byte. Where the C and this file
10
+ * disagree, the C is right; test/unit/scanner.test.ts diffs the two.
11
+ *
12
+ * Dependency-free and browser-safe: no Node imports.
13
+ */
14
+ export interface TexBlock {
15
+ kind: 'btex' | 'verbatimtex';
16
+ /**
17
+ * The block text exactly as mpto emits it between its wrapper lines:
18
+ * for `btex` leading and trailing whitespace is stripped; for the first
19
+ * `verbatimtex` block of a file only leading whitespace is stripped; later
20
+ * `verbatimtex` blocks are untouched. Line ends are normalised to "\n".
21
+ * The `%` that mpto appends to a `btex` body in TeX mode is NOT part of
22
+ * `body` (see `needsPercent` in mpto.ts) so the same block can be rendered
23
+ * in troff mode too.
24
+ */
25
+ body: string;
26
+ /** 1-based line of the opening keyword, counted the way mpto counts. */
27
+ line: number;
28
+ /** The file name the block was scanned from (used in `% line N file`). */
29
+ file: string;
30
+ /** Position of this block in source order, 0-based, counting both kinds. */
31
+ index: number;
32
+ /**
33
+ * The untrimmed text mpto accumulated for the block (its `res` buffer),
34
+ * before whitespace handling. Kept for callers that need the raw form.
35
+ */
36
+ raw: string;
37
+ /**
38
+ * Set when mpto would report an error for this block ("btex in TeX mode",
39
+ * "verbatimtex in TeX mode", "btex section does not end"). mpto still
40
+ * writes the wrapper lines for such a block but no body at all — not even
41
+ * the `%` — and `buildTexJob` reproduces that.
42
+ */
43
+ error?: string;
44
+ }
45
+ export interface ScanError {
46
+ message: string;
47
+ /** mpto's line counter at the time of the error (1-based). */
48
+ line: number;
49
+ file: string;
50
+ }
51
+ export interface ScanResult {
52
+ blocks: TexBlock[];
53
+ /** Every "makempx error:" mpto would print, in order. */
54
+ errors: ScanError[];
55
+ }
56
+ /**
57
+ * Scan MetaPost source for `btex ... etex` and `verbatimtex ... etex` blocks
58
+ * exactly as upstream mpto does, and report the errors mpto would report.
59
+ *
60
+ * One deliberate deviation: on an unterminated string literal upstream
61
+ * `mpx_mpto` loops forever printing "string does not end" (its do/while
62
+ * never advances once `mpx_getbta` has reached the end of the line). This
63
+ * port reports the error once and treats the rest of the line as the string.
64
+ */
65
+ export declare function scanTex(source: string, fileName: string): ScanResult;
66
+ /** The blocks of `scanTex`, for callers that do not need the error list. */
67
+ export declare function scanTexBlocks(source: string, fileName: string): TexBlock[];
68
+ /**
69
+ * Find the file names referenced by `input name` / `input "name"` statements
70
+ * (docs/05 §3.2). Lexical only: comments, string literals and the inside of
71
+ * btex/verbatimtex blocks are skipped, `input` must be a whole symbolic
72
+ * token, and the name is delimited the way `mp_scan_file_name` delimits it —
73
+ * it ends at a space, tab, `;`, `%` or end of line unless inside quotes, and
74
+ * the quotes themselves are not part of the name. Names are returned as
75
+ * written, in order, without deduplication; `scantokens`-built inputs are
76
+ * invisible to this function by design.
77
+ */
78
+ export declare function scanInputs(source: string): string[];