@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.
- package/LICENSE +674 -0
- package/NOTICE.md +30 -0
- package/README.md +127 -2
- package/package.json +56 -4
- package/runtime/LICENSE +165 -0
- package/runtime/NOTICE.md +32 -0
- package/runtime/dist/auto.d.ts +68 -0
- package/runtime/dist/auto.js +564 -0
- package/runtime/dist/bibtex.mjs +2 -0
- package/runtime/dist/bibtex.wasm +0 -0
- package/runtime/dist/bundles/cm-tfm/files.br +0 -0
- package/runtime/dist/bundles/cm-tfm/files.index.json +1 -0
- package/runtime/dist/bundles/cm-tfm/manifest.json +1 -0
- package/runtime/dist/bundles/cm-type1/files.br +0 -0
- package/runtime/dist/bundles/cm-type1/files.index.json +1 -0
- package/runtime/dist/bundles/cm-type1/manifest.json +1 -0
- package/runtime/dist/bundles/core/files.br +0 -0
- package/runtime/dist/bundles/core/files.index.json +1 -0
- package/runtime/dist/bundles/core/manifest.json +1 -0
- package/runtime/dist/bundles/hot.json +1 -0
- package/runtime/dist/bundles/index.json +80 -0
- package/runtime/dist/bundles/latex-core/files.br +0 -0
- package/runtime/dist/bundles/latex-core/files.index.json +1 -0
- package/runtime/dist/bundles/latex-core/manifest.json +1 -0
- package/runtime/dist/bundles/latex-extra/files.br +0 -0
- package/runtime/dist/bundles/latex-extra/files.index.json +1 -0
- package/runtime/dist/bundles/latex-extra/manifest.json +1 -0
- package/runtime/dist/bundles/lm-fonts/files.br +0 -0
- package/runtime/dist/bundles/lm-fonts/files.index.json +1 -0
- package/runtime/dist/bundles/lm-fonts/manifest.json +1 -0
- package/runtime/dist/bundles/luatex/files.br +0 -0
- package/runtime/dist/bundles/luatex/files.index.json +1 -0
- package/runtime/dist/bundles/luatex/manifest.json +1 -0
- package/runtime/dist/bundles/ps-fonts/files.br +0 -0
- package/runtime/dist/bundles/ps-fonts/files.index.json +1 -0
- package/runtime/dist/bundles/ps-fonts/manifest.json +1 -0
- package/runtime/dist/bundles/tex-plain/files.br +0 -0
- package/runtime/dist/bundles/tex-plain/files.index.json +1 -0
- package/runtime/dist/bundles/tex-plain/manifest.json +1 -0
- package/runtime/dist/bundles/tikz-snapshot/files.br +0 -0
- package/runtime/dist/bundles/tikz-snapshot/files.index.json +1 -0
- package/runtime/dist/bundles/tikz-snapshot/manifest.json +1 -0
- package/runtime/dist/bundles-config.d.ts +3 -0
- package/runtime/dist/bundles-config.js +16 -0
- package/runtime/dist/cli.d.ts +2 -0
- package/runtime/dist/cli.js +372 -0
- package/runtime/dist/core.d.ts +124 -0
- package/runtime/dist/core.js +927 -0
- package/runtime/dist/diagnostics.d.ts +53 -0
- package/runtime/dist/diagnostics.js +184 -0
- package/runtime/dist/dvisvgm.d.ts +20 -0
- package/runtime/dist/dvisvgm.js +46 -0
- package/runtime/dist/dvisvgm.mjs +2 -0
- package/runtime/dist/dvisvgm.wasm +0 -0
- package/runtime/dist/figures.d.ts +92 -0
- package/runtime/dist/figures.js +250 -0
- package/runtime/dist/ghostscript.d.ts +92 -0
- package/runtime/dist/ghostscript.js +218 -0
- package/runtime/dist/index.d.ts +87 -0
- package/runtime/dist/index.js +364 -0
- package/runtime/dist/logger.d.ts +48 -0
- package/runtime/dist/logger.js +55 -0
- package/runtime/dist/luatex.mjs +2 -0
- package/runtime/dist/luatex.wasm +0 -0
- package/runtime/dist/makeindex.mjs +2 -0
- package/runtime/dist/makeindex.wasm +0 -0
- package/runtime/dist/mplib.d.ts +106 -0
- package/runtime/dist/mplib.js +85 -0
- package/runtime/dist/mplib.mjs +2 -0
- package/runtime/dist/mplib.wasm +0 -0
- package/runtime/dist/node.d.ts +4 -0
- package/runtime/dist/node.js +49 -0
- package/runtime/dist/postscript.d.ts +21 -0
- package/runtime/dist/postscript.js +130 -0
- package/runtime/dist/prerender.d.ts +55 -0
- package/runtime/dist/prerender.js +106 -0
- package/runtime/dist/render/svg.d.ts +20 -0
- package/runtime/dist/render/svg.js +96 -0
- package/runtime/dist/tex/bridge.d.ts +93 -0
- package/runtime/dist/tex/bridge.js +223 -0
- package/runtime/dist/tex/cache-key.d.ts +33 -0
- package/runtime/dist/tex/cache-key.js +150 -0
- package/runtime/dist/tex/mpto.d.ts +60 -0
- package/runtime/dist/tex/mpto.js +112 -0
- package/runtime/dist/tex/mpx.d.ts +25 -0
- package/runtime/dist/tex/mpx.js +54 -0
- package/runtime/dist/tex/scanner.d.ts +78 -0
- package/runtime/dist/tex/scanner.js +326 -0
- package/runtime/dist/tex.mjs +2 -0
- package/runtime/dist/tex.wasm +0 -0
- package/runtime/dist/texengine.d.ts +35 -0
- package/runtime/dist/texengine.js +57 -0
- package/runtime/dist/texutils.d.ts +19 -0
- package/runtime/dist/texutils.js +60 -0
- package/runtime/dist/types.d.ts +376 -0
- package/runtime/dist/types.js +6 -0
- package/runtime/dist/vfs/bundle.d.ts +88 -0
- package/runtime/dist/vfs/bundle.js +315 -0
- package/runtime/dist/vfs/lazyfs.d.ts +62 -0
- package/runtime/dist/vfs/lazyfs.js +133 -0
- package/runtime/dist/worker.d.ts +15 -0
- package/runtime/dist/worker.js +143 -0
- package/runtime/licenses/COPYING.AFPL +230 -0
- package/runtime/licenses/COPYING.makeindex +42 -0
- package/runtime/licenses/GPL-3.0.txt +674 -0
- package/runtime/licenses/LGPL-3.0.txt +165 -0
- package/runtime/licenses/OFL-1.1.txt +93 -0
- package/runtime/package.json +1 -0
- package/runtime/upstream.json +27 -0
- package/src/browser-worker.js +26 -0
- package/src/browser.d.ts +18 -0
- package/src/browser.js +68 -0
- package/src/deploy.d.ts +5 -0
- package/src/deploy.js +48 -0
- package/src/index.d.ts +28 -0
- package/src/index.js +103 -0
- package/src/worker.js +15 -0
- package/upstream.json +27 -0
|
@@ -0,0 +1,372 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* mpost-wasm — a drop-in for `mpost` for the common flag set (docs/08 §5).
|
|
4
|
+
*
|
|
5
|
+
* mpost-wasm [OPTION]... [MPNAME[.mp]] [COMMANDS]
|
|
6
|
+
* mpost-wasm --dvitomp DVINAME[.dvi] [MPXNAME[.mpx]]
|
|
7
|
+
* mpost-wasm --prerender [--figures=DIR | --base=DIR] [--force] [--dry-run] PAGE.html...
|
|
8
|
+
*/
|
|
9
|
+
import { MetaPost, LOG_LEVELS, DEFAULT_BUNDLES } from './index.js';
|
|
10
|
+
import { prerender } from './prerender.js';
|
|
11
|
+
const HELP = `Usage: mpost-wasm [OPTION]... [MPNAME[.mp]] [COMMANDS]
|
|
12
|
+
mpost-wasm --latex [OPTION]... DOC.tex
|
|
13
|
+
mpost-wasm --prerender [--figures=DIR | --base=DIR] [--force] [--dry-run] PAGE.html...
|
|
14
|
+
Run MetaPost (WebAssembly build) on MPNAME, writing output files to the
|
|
15
|
+
current directory like mpost does; or, with --latex, typeset a complete
|
|
16
|
+
LaTeX/TikZ (or plain TeX, with --plain) document with tex.wasm and convert
|
|
17
|
+
every page to DOC-<page>.svg with dvisvgm.wasm.
|
|
18
|
+
|
|
19
|
+
-interaction=MODE batchmode|nonstopmode|scrollmode (default nonstopmode)
|
|
20
|
+
-numbersystem=SYSTEM scaled|double|decimal (binary/interval are not built)
|
|
21
|
+
-jobname=STRING output name (default: basename of MPNAME)
|
|
22
|
+
-tex=PROGRAM tex|etex|latex|auto (default auto)
|
|
23
|
+
-s INTERNAL=VALUE set an internal, e.g. -s prologues=3 -s outputformat="svg"
|
|
24
|
+
-halt-on-error stop at the first error
|
|
25
|
+
-file-line-error (accepted; diagnostics always carry file:line)
|
|
26
|
+
-recorder write a .fls file listing every file opened
|
|
27
|
+
-T, -troff accepted; troff mode is unsupported and warns
|
|
28
|
+
--format=eps|svg|json output format (default eps; or set outputformat)
|
|
29
|
+
--texmf=DIR use a texmf directory (flattened layout) instead of bundles
|
|
30
|
+
--bundles=DIR directory containing the bundles (default: next to this package)
|
|
31
|
+
--stdout print the first figure to stdout instead of writing files
|
|
32
|
+
--latex DOC.tex -> DOC-1.svg, DOC-2.svg ... (LaTeX); --plain for plain TeX
|
|
33
|
+
--engine=NAME latex | lualatex | luatex | plain | tex | auto (default auto: lualatex when
|
|
34
|
+
the document uses graphdrawing or \directlua)
|
|
35
|
+
--fonts=paths|woff2 how text is emitted in --latex mode (default paths)
|
|
36
|
+
--pdf with --latex: write DOC.pdf (pdfTeX's or LuaTeX's own PDF output)
|
|
37
|
+
instead of SVG pages; with --stdout, the PDF goes to stdout
|
|
38
|
+
--opentype load the OpenType bundles so \\usepackage{fontspec} works under
|
|
39
|
+
--engine=lualatex; off by default because LaTeX initialises
|
|
40
|
+
luaotfload on every LuaTeX run once it can find it
|
|
41
|
+
--prerender typeset the <script type="text/tikz|metapost">, <tikz-diagram> and
|
|
42
|
+
<metapost-diagram> elements of each PAGE and save each as figure-HASH.svg in
|
|
43
|
+
the directory the page's auto.js loader names in data-figures (default
|
|
44
|
+
figures/, next to the page); the tags then load the files instead of running
|
|
45
|
+
the engines. Files that exist are kept: the name is the content.
|
|
46
|
+
--figures=DIR save every page's figures in DIR instead
|
|
47
|
+
--base=DIR resolve each page's data-figures against DIR instead of the page's own
|
|
48
|
+
directory (for a rendered copy of a page saved elsewhere)
|
|
49
|
+
--force re-render figures whose file exists
|
|
50
|
+
--dry-run list what would be rendered, render nothing
|
|
51
|
+
-v, -vv, -vvv more on stderr: timings; the engines' output as it runs; every file
|
|
52
|
+
-q, --quiet nothing on stderr (not even TeX errors)
|
|
53
|
+
--log-level=LEVEL silent|error|warn|info|debug|trace (default warn)
|
|
54
|
+
-help, -version
|
|
55
|
+
`;
|
|
56
|
+
function parseArgs(argv) {
|
|
57
|
+
const o = {
|
|
58
|
+
interaction: 'nonstop',
|
|
59
|
+
numbersystem: 'scaled',
|
|
60
|
+
jobname: '', tex: 'auto', internals: {},
|
|
61
|
+
halt: false, recorder: false, troff: false, format: '', texmf: '', bundles: '', opentype: false,
|
|
62
|
+
stdout: false, file: '', commands: '', help: false, version: false, dvitomp: false,
|
|
63
|
+
latex: false, plain: false, pdf: false, engine: 'auto', fonts: 'paths',
|
|
64
|
+
verbose: 0, quiet: false, logLevel: '',
|
|
65
|
+
prerender: false, figures: '', base: '', force: false, dryRun: false, args: [],
|
|
66
|
+
};
|
|
67
|
+
const rest = [];
|
|
68
|
+
for (let i = 0; i < argv.length; i++) {
|
|
69
|
+
const a = argv[i];
|
|
70
|
+
const next = () => argv[++i];
|
|
71
|
+
const kv = (a.startsWith('--') ? a.slice(2) : a.slice(1));
|
|
72
|
+
const [k, v] = kv.includes('=') ? [kv.slice(0, kv.indexOf('=')), kv.slice(kv.indexOf('=') + 1)] : [kv, undefined];
|
|
73
|
+
if (!a.startsWith('-') || a === '-') {
|
|
74
|
+
rest.push(a);
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
switch (k) {
|
|
78
|
+
case 'interaction':
|
|
79
|
+
o.interaction = (v ?? next()).replace('mode', '');
|
|
80
|
+
break;
|
|
81
|
+
case 'numbersystem':
|
|
82
|
+
o.numbersystem = (v ?? next());
|
|
83
|
+
break;
|
|
84
|
+
case 'jobname':
|
|
85
|
+
o.jobname = v ?? next();
|
|
86
|
+
break;
|
|
87
|
+
case 'tex': {
|
|
88
|
+
const t = v ?? next();
|
|
89
|
+
o.tex = t === 'tex' ? 'plain' : t;
|
|
90
|
+
break;
|
|
91
|
+
}
|
|
92
|
+
case 's': {
|
|
93
|
+
const s = v ?? next();
|
|
94
|
+
const eq = s.indexOf('=');
|
|
95
|
+
const name = s.slice(0, eq);
|
|
96
|
+
let val = s.slice(eq + 1);
|
|
97
|
+
if (/^".*"$/.test(val))
|
|
98
|
+
val = val.slice(1, -1);
|
|
99
|
+
else if (!isNaN(Number(val)))
|
|
100
|
+
val = Number(val);
|
|
101
|
+
o.internals[name] = val;
|
|
102
|
+
break;
|
|
103
|
+
}
|
|
104
|
+
case 'halt-on-error':
|
|
105
|
+
o.halt = true;
|
|
106
|
+
break;
|
|
107
|
+
case 'file-line-error':
|
|
108
|
+
case 'file-line-error-style': break;
|
|
109
|
+
case 'no-file-line-error': break;
|
|
110
|
+
case 'recorder':
|
|
111
|
+
o.recorder = true;
|
|
112
|
+
break;
|
|
113
|
+
case 'T':
|
|
114
|
+
case 'troff':
|
|
115
|
+
o.troff = true;
|
|
116
|
+
break;
|
|
117
|
+
case 'format':
|
|
118
|
+
o.format = (v ?? next());
|
|
119
|
+
break;
|
|
120
|
+
case 'texmf':
|
|
121
|
+
o.texmf = v ?? next();
|
|
122
|
+
break;
|
|
123
|
+
case 'bundles':
|
|
124
|
+
o.bundles = v ?? next();
|
|
125
|
+
break;
|
|
126
|
+
case 'opentype':
|
|
127
|
+
o.opentype = true;
|
|
128
|
+
break;
|
|
129
|
+
case 'stdout':
|
|
130
|
+
o.stdout = true;
|
|
131
|
+
break;
|
|
132
|
+
case 'pdf':
|
|
133
|
+
o.pdf = true;
|
|
134
|
+
break;
|
|
135
|
+
case 'help':
|
|
136
|
+
o.help = true;
|
|
137
|
+
break;
|
|
138
|
+
case 'version':
|
|
139
|
+
o.version = true;
|
|
140
|
+
break;
|
|
141
|
+
case 'dvitomp':
|
|
142
|
+
o.dvitomp = true;
|
|
143
|
+
break;
|
|
144
|
+
case 'latex':
|
|
145
|
+
o.latex = true;
|
|
146
|
+
break;
|
|
147
|
+
case 'plain':
|
|
148
|
+
o.plain = true;
|
|
149
|
+
o.latex = true;
|
|
150
|
+
o.engine = 'plain';
|
|
151
|
+
break;
|
|
152
|
+
case 'engine':
|
|
153
|
+
o.engine = (v ?? next());
|
|
154
|
+
o.latex = true;
|
|
155
|
+
break;
|
|
156
|
+
case 'fonts':
|
|
157
|
+
o.fonts = (v ?? next());
|
|
158
|
+
break;
|
|
159
|
+
case 'prerender':
|
|
160
|
+
o.prerender = true;
|
|
161
|
+
break;
|
|
162
|
+
case 'figures':
|
|
163
|
+
o.figures = v ?? next();
|
|
164
|
+
break;
|
|
165
|
+
case 'base':
|
|
166
|
+
o.base = v ?? next();
|
|
167
|
+
break;
|
|
168
|
+
case 'force':
|
|
169
|
+
o.force = true;
|
|
170
|
+
break;
|
|
171
|
+
case 'dry-run':
|
|
172
|
+
o.dryRun = true;
|
|
173
|
+
break;
|
|
174
|
+
case 'v':
|
|
175
|
+
case 'verbose':
|
|
176
|
+
o.verbose++;
|
|
177
|
+
break;
|
|
178
|
+
case 'vv':
|
|
179
|
+
o.verbose += 2;
|
|
180
|
+
break;
|
|
181
|
+
case 'vvv':
|
|
182
|
+
o.verbose += 3;
|
|
183
|
+
break;
|
|
184
|
+
case 'q':
|
|
185
|
+
case 'quiet':
|
|
186
|
+
o.quiet = true;
|
|
187
|
+
break;
|
|
188
|
+
case 'log-level':
|
|
189
|
+
o.logLevel = (v ?? next());
|
|
190
|
+
break;
|
|
191
|
+
case 'ini':
|
|
192
|
+
case 'mem':
|
|
193
|
+
case 'progname':
|
|
194
|
+
case 'kpathsea-debug':
|
|
195
|
+
case 'restricted':
|
|
196
|
+
case 'debug':
|
|
197
|
+
case 'translate-file':
|
|
198
|
+
case '8bit':
|
|
199
|
+
console.error(`mpost-wasm: warning: option -${k} is accepted and ignored`);
|
|
200
|
+
if (v === undefined && ['mem', 'progname', 'kpathsea-debug', 'translate-file'].includes(k))
|
|
201
|
+
next();
|
|
202
|
+
break;
|
|
203
|
+
default:
|
|
204
|
+
console.error(`mpost-wasm: unknown option ${a}`);
|
|
205
|
+
process.exit(1);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
o.args = [...rest];
|
|
209
|
+
if (rest.length) {
|
|
210
|
+
const first = rest[0];
|
|
211
|
+
if (first.startsWith('&')) {
|
|
212
|
+
rest.shift();
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
if (rest.length) {
|
|
216
|
+
o.file = rest.shift();
|
|
217
|
+
}
|
|
218
|
+
o.commands = rest.join(' ');
|
|
219
|
+
return o;
|
|
220
|
+
}
|
|
221
|
+
async function main() {
|
|
222
|
+
const fs = await import('node:fs');
|
|
223
|
+
const path = await import('node:path');
|
|
224
|
+
const o = parseArgs(process.argv.slice(2));
|
|
225
|
+
if (o.help) {
|
|
226
|
+
process.stdout.write(HELP);
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
const logLevel = o.logLevel || (o.quiet ? 'silent' : o.verbose >= 3 ? 'trace' : o.verbose === 2 ? 'debug' : o.verbose === 1 ? 'info' : 'warn');
|
|
230
|
+
if (!LOG_LEVELS.includes(logLevel)) {
|
|
231
|
+
console.error(`mpost-wasm: unknown log level ${logLevel} (${LOG_LEVELS.join(', ')})`);
|
|
232
|
+
process.exit(1);
|
|
233
|
+
}
|
|
234
|
+
// Everything the level admits goes to stderr, so stdout stays the transcript (and, with --stdout, the figure).
|
|
235
|
+
// MetaPost's own errors and warnings are left out: the transcript on stdout carries them, as with mpost.
|
|
236
|
+
const logger = (r) => {
|
|
237
|
+
if (!o.prerender && r.source === 'metapost' && (r.level === 'error' || r.level === 'warn'))
|
|
238
|
+
return;
|
|
239
|
+
const raw = (r.level === 'debug' || r.level === 'trace') && r.source !== 'host';
|
|
240
|
+
process.stderr.write(raw ? `${r.source}: ${r.message}\n` : `mpost-wasm: ${r.source === 'host' ? '' : r.source + ': '}${r.message}\n`);
|
|
241
|
+
};
|
|
242
|
+
if (o.prerender) {
|
|
243
|
+
// the browser's defaults (deterministic, seed 42), so the files are the bytes the tags would produce
|
|
244
|
+
const pages = o.args.filter((a) => !a.startsWith('&'));
|
|
245
|
+
if (!pages.length) {
|
|
246
|
+
console.error('mpost-wasm: --prerender needs one or more .html pages');
|
|
247
|
+
process.exit(1);
|
|
248
|
+
}
|
|
249
|
+
for (const p of pages)
|
|
250
|
+
if (!fs.existsSync(p)) {
|
|
251
|
+
console.error(`mpost-wasm: cannot open ${p}`);
|
|
252
|
+
process.exit(1);
|
|
253
|
+
}
|
|
254
|
+
// each page's own data-bundles decide its engine's bundles, as in the browser; --opentype adds to them
|
|
255
|
+
const r = await prerender(pages, {
|
|
256
|
+
createOptions: { texmfDir: o.texmf || undefined, bundleBaseUrl: o.bundles ? 'file://' + path.resolve(o.bundles) + '/' : undefined, logLevel, logger },
|
|
257
|
+
addBundles: o.opentype ? ['opentype', 'otf-fonts'] : undefined,
|
|
258
|
+
figuresDir: o.figures ? path.resolve(o.figures) : undefined, baseDir: o.base ? path.resolve(o.base) : undefined,
|
|
259
|
+
force: o.force, dryRun: o.dryRun, report: (l) => process.stdout.write(l + '\n'),
|
|
260
|
+
});
|
|
261
|
+
const pending = r.entries.filter((e) => e.status === 'pending').length;
|
|
262
|
+
process.stdout.write(`mpost-wasm: ${r.rendered} rendered, ${r.existing} already saved, ${r.failed} failed${o.dryRun ? `, ${pending} to render` : ''}\n`);
|
|
263
|
+
process.exit(r.failed ? 1 : 0);
|
|
264
|
+
}
|
|
265
|
+
const mp = await MetaPost.create({
|
|
266
|
+
numberSystem: o.numbersystem, tex: o.tex, interaction: o.interaction, haltOnError: o.halt,
|
|
267
|
+
texmfDir: o.texmf || undefined, bundleBaseUrl: o.bundles ? 'file://' + path.resolve(o.bundles) + '/' : undefined,
|
|
268
|
+
bundles: o.opentype ? [...DEFAULT_BUNDLES, 'opentype', 'otf-fonts'] : undefined,
|
|
269
|
+
deterministic: false, logLevel, logger,
|
|
270
|
+
});
|
|
271
|
+
if (o.version) {
|
|
272
|
+
console.log(`MetaPost ${mp.version.metapost} (mp-tikz-wasm) with ${mp.version.tex}`);
|
|
273
|
+
mp.dispose();
|
|
274
|
+
return;
|
|
275
|
+
}
|
|
276
|
+
if (o.troff)
|
|
277
|
+
console.error('mpost-wasm: warning: troff mode is not supported; continuing in TeX mode');
|
|
278
|
+
if (o.latex) {
|
|
279
|
+
if (!o.file) {
|
|
280
|
+
console.error('mpost-wasm: --latex needs a .tex file');
|
|
281
|
+
process.exit(1);
|
|
282
|
+
}
|
|
283
|
+
const f = fs.existsSync(o.file) ? o.file : fs.existsSync(o.file + '.tex') ? o.file + '.tex' : null;
|
|
284
|
+
if (!f) {
|
|
285
|
+
console.error(`mpost-wasm: cannot open ${o.file}`);
|
|
286
|
+
process.exit(1);
|
|
287
|
+
}
|
|
288
|
+
const job = o.jobname || path.basename(f).replace(/\.tex$/, '');
|
|
289
|
+
const sib = {};
|
|
290
|
+
// sibling inputs; PNG and JPEG too, which pdfTeX and LuaTeX include in PDF output
|
|
291
|
+
for (const e of fs.readdirSync(path.dirname(f)))
|
|
292
|
+
if (e !== path.basename(f) && /\.(tex|sty|cls|def|clo|fd|eps|dat|csv|txt|bib|png|jpe?g)$/i.test(e))
|
|
293
|
+
sib[e] = fs.readFileSync(path.join(path.dirname(f), e));
|
|
294
|
+
const r = await mp.latex(fs.readFileSync(f, 'utf8'), { engine: o.engine, jobName: job, files: sib, fonts: o.fonts, output: o.pdf ? 'pdf' : 'svg' });
|
|
295
|
+
// with --pdf --stdout, stdout is the PDF alone: the transcript goes to stderr instead
|
|
296
|
+
(o.pdf && o.stdout ? process.stderr : process.stdout).write(r.log.endsWith('\n') ? r.log : r.log + '\n');
|
|
297
|
+
if (o.pdf) {
|
|
298
|
+
if (r.pdf) {
|
|
299
|
+
if (o.stdout)
|
|
300
|
+
process.stdout.write(r.pdf);
|
|
301
|
+
else
|
|
302
|
+
fs.writeFileSync(`${job}.pdf`, r.pdf);
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
else if (o.stdout) {
|
|
306
|
+
if (r.pages[0])
|
|
307
|
+
process.stdout.write(r.pages[0]);
|
|
308
|
+
}
|
|
309
|
+
else
|
|
310
|
+
r.pages.forEach((svg, i) => fs.writeFileSync(`${job}-${i + 1}.svg`, svg));
|
|
311
|
+
fs.writeFileSync(`${job}.log`, r.texLog);
|
|
312
|
+
mp.dispose();
|
|
313
|
+
process.exit(r.status === 'error' || r.status === 'fatal' ? 1 : 0);
|
|
314
|
+
}
|
|
315
|
+
let source;
|
|
316
|
+
let jobname = o.jobname;
|
|
317
|
+
let files = {};
|
|
318
|
+
if (o.file) {
|
|
319
|
+
const f = fs.existsSync(o.file) ? o.file : fs.existsSync(o.file + '.mp') ? o.file + '.mp' : null;
|
|
320
|
+
if (!f) {
|
|
321
|
+
console.error(`mpost-wasm: cannot open ${o.file}`);
|
|
322
|
+
process.exit(1);
|
|
323
|
+
}
|
|
324
|
+
source = fs.readFileSync(f, 'utf8');
|
|
325
|
+
if (!jobname)
|
|
326
|
+
jobname = path.basename(f).replace(/\.mp$/, '');
|
|
327
|
+
// make sibling .mp files inputtable
|
|
328
|
+
for (const e of fs.readdirSync(path.dirname(f)))
|
|
329
|
+
if (/\.(mp|tex|dat|txt)$/.test(e) && e !== path.basename(f))
|
|
330
|
+
files[e] = fs.readFileSync(path.join(path.dirname(f), e));
|
|
331
|
+
if (o.commands)
|
|
332
|
+
source += '\n' + o.commands;
|
|
333
|
+
}
|
|
334
|
+
else if (o.commands) {
|
|
335
|
+
source = o.commands;
|
|
336
|
+
if (!jobname)
|
|
337
|
+
jobname = 'mpout';
|
|
338
|
+
}
|
|
339
|
+
else {
|
|
340
|
+
source = fs.readFileSync(0, 'utf8');
|
|
341
|
+
if (!jobname)
|
|
342
|
+
jobname = 'mpout';
|
|
343
|
+
}
|
|
344
|
+
const internals = { ...o.internals };
|
|
345
|
+
let format = o.format || 'eps';
|
|
346
|
+
if (!o.format && typeof internals.outputformat === 'string')
|
|
347
|
+
format = internals.outputformat === 'svg' ? 'svg' : 'eps';
|
|
348
|
+
const prologues = typeof internals.prologues === 'number' ? internals.prologues : format === 'svg' ? 3 : 0;
|
|
349
|
+
const r = await mp.run(source, { jobName: jobname, files, internals, format, prologues, svg: { precision: false, idPrefix: false } });
|
|
350
|
+
process.stdout.write(r.log.endsWith('\n') ? r.log : r.log + '\n'); // MetaPost's terminal transcript, like mpost
|
|
351
|
+
// like mpost: the default outputtemplate is %j.%c whatever the format; %o expands to the format's extension
|
|
352
|
+
const ext = format === 'svg' ? 'svg' : format === 'json' ? 'json' : 'eps';
|
|
353
|
+
const template = typeof internals.outputtemplate === 'string' ? internals.outputtemplate : `%j.%c`;
|
|
354
|
+
for (const fig of r.figures) {
|
|
355
|
+
const name = template.replace('%j', jobname).replace('%c', String(fig.charcode)).replace('%o', ext);
|
|
356
|
+
const body = format === 'svg' ? fig.svg : format === 'json' ? JSON.stringify(fig.json) : fig.eps;
|
|
357
|
+
if (o.stdout) {
|
|
358
|
+
process.stdout.write(body);
|
|
359
|
+
break;
|
|
360
|
+
}
|
|
361
|
+
fs.writeFileSync(name, body);
|
|
362
|
+
}
|
|
363
|
+
for (const [name, data] of Object.entries(r.artifacts))
|
|
364
|
+
if (name !== `${jobname}.log` || true)
|
|
365
|
+
fs.writeFileSync(name, data);
|
|
366
|
+
if (o.recorder)
|
|
367
|
+
fs.writeFileSync(`${jobname}.fls`, r.diagnostics.map(() => '').join(''));
|
|
368
|
+
mp.dispose();
|
|
369
|
+
process.exit(r.history >= 2 ? 1 : 0);
|
|
370
|
+
}
|
|
371
|
+
main().catch((e) => { console.error(e?.stack ?? String(e)); process.exit(2); });
|
|
372
|
+
//# sourceMappingURL=cli.js.map
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* core.ts — the orchestrator (docs/01 §3). Runs in a Worker (browser) or on
|
|
3
|
+
* the main thread (Node). Owns one mplib.wasm instance for its lifetime and
|
|
4
|
+
* creates one tex.wasm instance per TeX invocation.
|
|
5
|
+
*/
|
|
6
|
+
import { type MplibModule, type MplibFactory } from './mplib.js';
|
|
7
|
+
import { type TexFactory } from './texengine.js';
|
|
8
|
+
import { type DvisvgmFactory } from './dvisvgm.js';
|
|
9
|
+
import type { Ghostscript } from './ghostscript.js';
|
|
10
|
+
import { BundleSet } from './vfs/bundle.js';
|
|
11
|
+
import { Logger } from './logger.js';
|
|
12
|
+
import type { MetaPostOptions, RunOptions, RunResult, Diagnostic, ProgressEvent, LatexRunOptions, LatexResult, BibtexOptions, MakeindexOptions, ToolResult } from './types.js';
|
|
13
|
+
/** The local references (href="#id") in an SVG that name no element in it. */
|
|
14
|
+
export declare function danglingRefs(svg: string): string[];
|
|
15
|
+
/** Where MetaPost looks for each file type (docs/06 §2). */
|
|
16
|
+
export declare const SEARCH_PATHS: Record<number, string[]>;
|
|
17
|
+
export interface CoreEnv {
|
|
18
|
+
mplibFactory: MplibFactory;
|
|
19
|
+
texFactory?: TexFactory;
|
|
20
|
+
/** luatex.wasm, same module interface as tex.wasm; needed for engine 'lualatex' / 'luatex' */
|
|
21
|
+
luatexFactory?: TexFactory;
|
|
22
|
+
dvisvgmFactory?: DvisvgmFactory;
|
|
23
|
+
/** the Ghostscript module for dvisvgm's PostScript (ghostscript.ts), when enabled; null if it cannot be loaded */
|
|
24
|
+
ghostscript?: () => Promise<Ghostscript | null>;
|
|
25
|
+
/** bibtex.wasm and makeindex.wasm, loaded on first use; undefined if this build has none */
|
|
26
|
+
tools?: (name: 'bibtex' | 'makeindex') => Promise<TexFactory | undefined>;
|
|
27
|
+
/** bundles merged into /texmf (browser, or Node without texmfDir) */
|
|
28
|
+
bundles?: BundleSet;
|
|
29
|
+
/** Node: real directory mounted at /texmf with NODEFS */
|
|
30
|
+
texmfDir?: string;
|
|
31
|
+
options: MetaPostOptions;
|
|
32
|
+
onProgress?: (e: ProgressEvent) => void;
|
|
33
|
+
onLog?: (line: string) => void;
|
|
34
|
+
/** where every part of a run reports (docs/08 §4); silent if absent */
|
|
35
|
+
logger?: Logger;
|
|
36
|
+
}
|
|
37
|
+
export declare class MetaPostCore {
|
|
38
|
+
private env;
|
|
39
|
+
M: MplibModule;
|
|
40
|
+
private cache;
|
|
41
|
+
private bridge?;
|
|
42
|
+
readonly version: {
|
|
43
|
+
metapost: string;
|
|
44
|
+
tex: string;
|
|
45
|
+
build: string;
|
|
46
|
+
};
|
|
47
|
+
private userFiles;
|
|
48
|
+
/**
|
|
49
|
+
* luaotfload's font cache (TEXMF_VAR), carried from one LuaTeX run to the
|
|
50
|
+
* next. Parsing an OpenType face costs about a second, and every TeX run
|
|
51
|
+
* gets a fresh filesystem, so without this each render pays that again.
|
|
52
|
+
*/
|
|
53
|
+
private texmfVar;
|
|
54
|
+
private get logger();
|
|
55
|
+
constructor(env: CoreEnv);
|
|
56
|
+
init(): Promise<void>;
|
|
57
|
+
private progress;
|
|
58
|
+
/** The errors and warnings of a finished run, one record each; a MetaPost help paragraph is indented under its error. */
|
|
59
|
+
private report;
|
|
60
|
+
private formatId;
|
|
61
|
+
private installTexmf;
|
|
62
|
+
/** Restore the font cache this instance has accumulated (LuaTeX runs only). */
|
|
63
|
+
private installTexmfVar;
|
|
64
|
+
/**
|
|
65
|
+
* Start the font cache from the prebuilt name database, if the tree has one
|
|
66
|
+
* (the `opentype` bundle, or a mounted texmfDir built by make-fontdb). Its
|
|
67
|
+
* paths are the /texmf ones every instance sees; luaotfload checks only the
|
|
68
|
+
* index version, and rescans by itself when a lookup misses.
|
|
69
|
+
*/
|
|
70
|
+
private seedFontDb;
|
|
71
|
+
/** Keep whatever luaotfload wrote, so the next run starts warm. */
|
|
72
|
+
private collectTexmfVar;
|
|
73
|
+
private copyUserFiles;
|
|
74
|
+
/** Last-chance file resolution when the C search failed: consult bundles, then the user hook. */
|
|
75
|
+
private findFile;
|
|
76
|
+
private findFileImpl;
|
|
77
|
+
private chain;
|
|
78
|
+
private misses;
|
|
79
|
+
private stats;
|
|
80
|
+
private engine;
|
|
81
|
+
private texDiagnostics;
|
|
82
|
+
private makeText;
|
|
83
|
+
private runScript;
|
|
84
|
+
addFiles(files: Record<string, string | Uint8Array>): void;
|
|
85
|
+
/**
|
|
86
|
+
* Take files off this instance that addFiles (or a run's `files`) put there, named as they were
|
|
87
|
+
* given; with no names, all of them. They otherwise stay for the instance's lifetime, so a host
|
|
88
|
+
* typesetting one document after another would hand the first one's .aux to the next.
|
|
89
|
+
*/
|
|
90
|
+
removeFiles(names?: string[]): void;
|
|
91
|
+
clearCache(): void;
|
|
92
|
+
run(source: string, ro?: RunOptions): Promise<RunResult>;
|
|
93
|
+
private lastTexLog;
|
|
94
|
+
private recentOutput;
|
|
95
|
+
/**
|
|
96
|
+
* Typeset a complete LaTeX (or plain TeX) document with tex.wasm and convert
|
|
97
|
+
* every page to SVG with dvisvgm.wasm — the TikZ/PGF pipeline. Text becomes
|
|
98
|
+
* glyph outlines unless fonts: 'woff2' is requested.
|
|
99
|
+
*/
|
|
100
|
+
latex(source: string, lo?: LatexRunOptions): Promise<LatexResult>;
|
|
101
|
+
/**
|
|
102
|
+
* BibTeX (bibtex.wasm) on the .aux a latex() run wrote: doc.bbl and doc.blg come back in
|
|
103
|
+
* artifacts, for the next latex() run. Like latex(), a fresh module each time.
|
|
104
|
+
*/
|
|
105
|
+
bibtex(bo?: BibtexOptions): Promise<ToolResult>;
|
|
106
|
+
/** makeindex (makeindex.wasm) on the .idx a latex() run wrote: doc.ind and doc.ilg come back in artifacts. */
|
|
107
|
+
makeindex(mo?: MakeindexOptions): Promise<ToolResult>;
|
|
108
|
+
/** One run of a TeX Live utility: what it wrote in /work comes back, its transcript as diagnostics. */
|
|
109
|
+
private runTool;
|
|
110
|
+
private hasSnapshot;
|
|
111
|
+
private typeset;
|
|
112
|
+
private runMetaPost;
|
|
113
|
+
/**
|
|
114
|
+
* Scan the job and every `input`-ed file reachable from it (lexically),
|
|
115
|
+
* splicing each input file's blocks in at the line of the `input` statement
|
|
116
|
+
* so the verbatimtex chain seen by the pre-scan matches the run's order.
|
|
117
|
+
*/
|
|
118
|
+
private scanAll;
|
|
119
|
+
private resolveInput;
|
|
120
|
+
}
|
|
121
|
+
/** Errors (`! ...` + `l.N`) and package warnings from a TeX transcript, attributed to the document. */
|
|
122
|
+
export declare function parseTexLogDocument(log: string, file: string): Diagnostic[];
|
|
123
|
+
/** Does a LaTeX source need LuaTeX? (TikZ graphdrawing, \directlua, luacode.) Used by engine 'auto'. */
|
|
124
|
+
export declare function needsLuaTeX(source: string): boolean;
|