@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,250 @@
|
|
|
1
|
+
import { sha256 } from './tex/cache-key.js';
|
|
2
|
+
/** Is this TikZ text a complete document (LaTeX with \documentclass, or plain TeX ending in \bye) rather than a body to wrap? */
|
|
3
|
+
export function isCompleteDocument(source) {
|
|
4
|
+
return /\\documentclass|\\bye\b/.test(source);
|
|
5
|
+
}
|
|
6
|
+
/** Wrap a TikZ body in a standalone document unless it already is one. */
|
|
7
|
+
export function wrapTikz(source, attrs = {}) {
|
|
8
|
+
if (isCompleteDocument(source))
|
|
9
|
+
return source;
|
|
10
|
+
const libs = (attrs.libraries ?? '').split(/[,\s]+/).filter(Boolean);
|
|
11
|
+
const gd = (attrs.gdlibraries ?? '').split(/[,\s]+/).filter(Boolean);
|
|
12
|
+
// graph drawing is used through \graph, which the graphs library provides
|
|
13
|
+
for (const l of ['graphs', 'graphdrawing'])
|
|
14
|
+
if (gd.length && !libs.includes(l))
|
|
15
|
+
libs.push(l);
|
|
16
|
+
const pkgs = (attrs.packages ?? '').split(/[,\s]+/).filter(Boolean);
|
|
17
|
+
// A body that is already a picture is not wrapped in another. Environments that
|
|
18
|
+
// draw their own tikzpicture -- tikzcd, circuitikz, chemfig's \chemfig and
|
|
19
|
+
// \schemestart -- break when nested in a second one: the page keeps its size but
|
|
20
|
+
// the drawing collapses to its edge, or vanishes. Standalone's `tikz` option,
|
|
21
|
+
// though, crops only tikzpicture environments, and left bare under it a circuitikz
|
|
22
|
+
// comes out as a whole letter page; so such a body gets the plain class, which
|
|
23
|
+
// crops whatever the body is, with TikZ loaded by hand. Only a body that *starts*
|
|
24
|
+
// with one is treated so (`\node {\chemfig{...}};` is TikZ code), and every
|
|
25
|
+
// other body keeps the document -- hence the figure hash -- it always had.
|
|
26
|
+
const ownPicture = /^\s*(\\begin\{(tikzcd|circuitikz)\}|\\chemfig\b|\\schemestart\b)/.test(source);
|
|
27
|
+
const isPicture = ownPicture || /\\begin\{tikzpicture\}|\\tikz\b|\\begin\{axis\}/.test(source);
|
|
28
|
+
const body = isPicture ? source : `\\begin{tikzpicture}\n${source}\n\\end{tikzpicture}`;
|
|
29
|
+
const border = attrs.border ?? '2pt';
|
|
30
|
+
return [
|
|
31
|
+
...(ownPicture ? [`\\documentclass[border=${border}]{standalone}`, '\\usepackage{tikz}'] : [`\\documentclass[tikz,border=${border}]{standalone}`]),
|
|
32
|
+
...(pkgs.length ? [`\\usepackage{${pkgs.join(',')}}`] : []),
|
|
33
|
+
...(libs.length ? [`\\usetikzlibrary{${libs.join(',')}}`] : []),
|
|
34
|
+
...(gd.length ? [`\\usegdlibrary{${gd.join(',')}}`] : []),
|
|
35
|
+
...(attrs.preamble ? [attrs.preamble] : []),
|
|
36
|
+
'\\begin{document}',
|
|
37
|
+
body,
|
|
38
|
+
'\\end{document}',
|
|
39
|
+
].join('\n');
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Wrap a MetaPost body in one figure unless it already has beginfig/endfig.
|
|
43
|
+
* `input` statements are hoisted out of the figure: `input boxes` inside a
|
|
44
|
+
* figure group makes MetaPost (native mpost too) recurse until its input stack
|
|
45
|
+
* overflows, and macro packages are meant to be loaded at top level anyway.
|
|
46
|
+
*/
|
|
47
|
+
export function wrapMetaPost(source, attrs = {}) {
|
|
48
|
+
const prologues = attrs.prologues ?? '3';
|
|
49
|
+
if (/\bbeginfig\s*\(/.test(source))
|
|
50
|
+
return `prologues:=${prologues};\n${source}`;
|
|
51
|
+
const inputs = [];
|
|
52
|
+
const body = source.split('\n').filter((line) => {
|
|
53
|
+
const m = /^\s*(input\s+[^;%]+;?)\s*(%.*)?$/.exec(line);
|
|
54
|
+
if (m) {
|
|
55
|
+
inputs.push(m[1].endsWith(';') ? m[1] : m[1] + ';');
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
return true;
|
|
59
|
+
}).join('\n');
|
|
60
|
+
return `prologues:=${prologues};\n${inputs.join('\n')}${inputs.length ? '\n' : ''}beginfig(1);\n${body}\nendfig;\nend.`;
|
|
61
|
+
}
|
|
62
|
+
/** The document the engines are given for an element. */
|
|
63
|
+
export function figureDocument(req) {
|
|
64
|
+
return req.kind === 'tikz' ? wrapTikz(req.source, req.attrs) : wrapMetaPost(req.source, req.attrs);
|
|
65
|
+
}
|
|
66
|
+
const HASH_SPACE = 2176782336n; // 36^6
|
|
67
|
+
/** Six lowercase base-36 characters identifying a figure (see the header). */
|
|
68
|
+
export function figureHash(req) {
|
|
69
|
+
const a = req.attrs;
|
|
70
|
+
const d = sha256(new TextEncoder().encode(`${req.kind}\0${a.fonts ?? ''}\0${a.tex ?? ''}\0${a.engine ?? ''}\0${figureDocument(req)}`));
|
|
71
|
+
let n = 0n;
|
|
72
|
+
for (let i = 0; i < 8; i++)
|
|
73
|
+
n = (n << 8n) | BigInt(d[i]);
|
|
74
|
+
return (n % HASH_SPACE).toString(36).padStart(6, '0');
|
|
75
|
+
}
|
|
76
|
+
/** `figure-HASH.svg`. */
|
|
77
|
+
export function figureName(hash) { return `figure-${hash}.svg`; }
|
|
78
|
+
/** Matches a saved figure's file name; group 1 is the hash. */
|
|
79
|
+
export const FIGURE_FILE = /^figure-([0-9a-z]{6})\.svg$/;
|
|
80
|
+
/** Does this text start like an SVG document (a server that answers every path with its index page does not)? */
|
|
81
|
+
export function isSvg(text) {
|
|
82
|
+
return /^\s*(?:<\?xml[^>]*\?>\s*)?(?:<!--[\s\S]*?-->\s*)*(?:<!DOCTYPE[^>]*>\s*)?<svg[\s>]/i.test(text);
|
|
83
|
+
}
|
|
84
|
+
/** Typeset one figure with the engine; the ids are prefixed with its hash so the SVG stands alone. */
|
|
85
|
+
export async function renderFigure(mp, req, hash = figureHash(req)) {
|
|
86
|
+
const doc = figureDocument(req);
|
|
87
|
+
const idPrefix = `mpw${hash}-`;
|
|
88
|
+
if (req.kind === 'tikz') {
|
|
89
|
+
const engine = req.attrs.engine ?? 'auto';
|
|
90
|
+
// A wrapped body is a standalone page with a border, and the SVG must be
|
|
91
|
+
// that page: dvisvgm's default box is the tight one PGF reports, which is
|
|
92
|
+
// the page minus the border — and not always all of the ink. PGF's
|
|
93
|
+
// classic arrow tips (`>=latex`, `stealth`, ...) declare no hull, so TikZ
|
|
94
|
+
// leaves them out of its bounding box (TeX Live 2025); natively the
|
|
95
|
+
// border is what keeps them on the page, and the tight crop cut them off
|
|
96
|
+
// (a `->` on a horizontal line rendered as a line with no head).
|
|
97
|
+
// `papersize` is the page standalone lays out, so the SVG is the PDF
|
|
98
|
+
// page. A complete document keeps the default: an article is not a page.
|
|
99
|
+
const bbox = isCompleteDocument(req.source) ? undefined : 'papersize';
|
|
100
|
+
const r = await mp.latex(doc, { fonts: req.attrs.fonts === 'woff2' ? 'woff2' : 'paths', engine, bbox, svg: { idPrefix, precision: false } });
|
|
101
|
+
return { svg: r.pages.join('\n'), log: r.log, diagnostics: r.diagnostics, ok: r.status === 'ok' && r.pages.length > 0, ms: r.stats.totalMs };
|
|
102
|
+
}
|
|
103
|
+
const tex = (req.attrs.tex ?? 'auto');
|
|
104
|
+
const r = await mp.run(doc, { format: 'svg', tex, svg: { idPrefix } });
|
|
105
|
+
// several beginfig blocks give several <svg> roots joined by newlines: exactly what the tags inject
|
|
106
|
+
return { svg: r.figures.map((f) => f.svg ?? '').join('\n'), log: r.log, diagnostics: r.diagnostics, ok: r.history < 2 && r.figures.length > 0, ms: r.stats.totalMs };
|
|
107
|
+
}
|
|
108
|
+
const SCRIPT_RE = /<script\b([^>]*)>([\s\S]*?)<\/script\s*>/gi;
|
|
109
|
+
const ELEMENT_RE = /<(tikz-diagram|metapost-diagram)\b([^>]*)>([\s\S]*?)<\/\1\s*>/gi;
|
|
110
|
+
const ATTR_RE = /([^\s"'<>/=]+)(?:\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s"'<>`]+)))?/g;
|
|
111
|
+
const ENTITY_RE = /&(?:#(\d+)|#x([0-9a-f]+)|(lt|gt|amp|quot|apos|nbsp));/gi;
|
|
112
|
+
const ENTITIES = { lt: '<', gt: '>', amp: '&', quot: '"', apos: "'", nbsp: ' ' };
|
|
113
|
+
/** Decode the entities an HTML parser would in text and attribute values. */
|
|
114
|
+
export function decodeEntities(s) {
|
|
115
|
+
if (!s.includes('&'))
|
|
116
|
+
return s;
|
|
117
|
+
return s.replace(ENTITY_RE, (_m, dec, hex, name) => dec ? String.fromCodePoint(Number(dec)) : hex ? String.fromCodePoint(parseInt(hex, 16)) : ENTITIES[name.toLowerCase()]);
|
|
118
|
+
}
|
|
119
|
+
/** Attributes of an opening tag, `data-` stripped, as the tags see them (`attrsOf` in auto.ts). */
|
|
120
|
+
export function parseAttributes(s) {
|
|
121
|
+
const out = {};
|
|
122
|
+
for (const m of s.matchAll(ATTR_RE))
|
|
123
|
+
out[m[1].toLowerCase().replace(/^data-/, '')] = decodeEntities(m[2] ?? m[3] ?? m[4] ?? '');
|
|
124
|
+
return out;
|
|
125
|
+
}
|
|
126
|
+
/** The text of an element as `textContent` gives it, then trimmed as the tags trim it. */
|
|
127
|
+
function normalize(text) { return text.replace(/^\s*\n/, '').replace(/\s+$/, ''); }
|
|
128
|
+
/** Every diagram element in a page, in document order. */
|
|
129
|
+
export function extractFigures(html) {
|
|
130
|
+
const found = [];
|
|
131
|
+
for (const m of html.matchAll(SCRIPT_RE)) {
|
|
132
|
+
const attrs = parseAttributes(m[1]);
|
|
133
|
+
const type = (attrs.type ?? '').trim().toLowerCase();
|
|
134
|
+
if (type !== 'text/tikz' && type !== 'text/metapost')
|
|
135
|
+
continue;
|
|
136
|
+
found.push({ kind: type === 'text/metapost' ? 'metapost' : 'tikz', source: normalize(m[2]), attrs, tag: 'script', offset: m.index });
|
|
137
|
+
}
|
|
138
|
+
for (const m of html.matchAll(ELEMENT_RE)) {
|
|
139
|
+
const tag = m[1].toLowerCase();
|
|
140
|
+
found.push({ kind: tag === 'metapost-diagram' ? 'metapost' : 'tikz', source: normalize(decodeEntities(m[3])), attrs: parseAttributes(m[2]), tag, offset: m.index });
|
|
141
|
+
}
|
|
142
|
+
return found.sort((a, b) => a.offset - b.offset);
|
|
143
|
+
}
|
|
144
|
+
/** The `data-*` attributes of the page's auto.js loader tag (`data-figures` → `figures`), or {} if there is none. */
|
|
145
|
+
/**
|
|
146
|
+
* A loader's data-bundles as a bundle list: names prefixed with + are added to the
|
|
147
|
+
* defaults (`+opentype`), a bare list replaces them; mixing the two is a replace
|
|
148
|
+
* with the +names appended. undefined when the attribute is absent or empty. The
|
|
149
|
+
* one parser for auto.js and the pre-renderer, so they can never load different sets.
|
|
150
|
+
*/
|
|
151
|
+
export function bundleList(spec, defaults) {
|
|
152
|
+
const names = (spec ?? '').split(/[,\s]+/).filter(Boolean);
|
|
153
|
+
if (!names.length)
|
|
154
|
+
return undefined;
|
|
155
|
+
const added = names.filter((n) => n.startsWith('+')).map((n) => n.slice(1));
|
|
156
|
+
const listed = names.filter((n) => !n.startsWith('+'));
|
|
157
|
+
return [...(listed.length ? listed : defaults), ...added];
|
|
158
|
+
}
|
|
159
|
+
export function loaderAttributes(html) {
|
|
160
|
+
const m = /<script\b([^>]*\bsrc\s*=\s*["'][^"']*auto\.js["'][^>]*)>/i.exec(html);
|
|
161
|
+
if (!m)
|
|
162
|
+
return {};
|
|
163
|
+
const out = {};
|
|
164
|
+
for (const a of m[1].matchAll(ATTR_RE))
|
|
165
|
+
if (a[1].toLowerCase().startsWith('data-'))
|
|
166
|
+
out[a[1].slice(5).toLowerCase()] = decodeEntities(a[2] ?? a[3] ?? a[4] ?? '');
|
|
167
|
+
return out;
|
|
168
|
+
}
|
|
169
|
+
// ----------------------------------------------------------------- a zip file
|
|
170
|
+
// Store-only (SVG compresses well, but the browsers that need the zip are the
|
|
171
|
+
// ones without a directory picker, and a dependency is not worth a few
|
|
172
|
+
// hundred kilobytes). Timestamps are fixed at 1980-01-01 so the archive is
|
|
173
|
+
// reproducible.
|
|
174
|
+
let crcTable = null;
|
|
175
|
+
export function crc32(data) {
|
|
176
|
+
if (!crcTable) {
|
|
177
|
+
crcTable = new Uint32Array(256);
|
|
178
|
+
for (let n = 0; n < 256; n++) {
|
|
179
|
+
let c = n;
|
|
180
|
+
for (let k = 0; k < 8; k++)
|
|
181
|
+
c = c & 1 ? 0xedb88320 ^ (c >>> 1) : c >>> 1;
|
|
182
|
+
crcTable[n] = c >>> 0;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
let c = 0xffffffff;
|
|
186
|
+
for (let i = 0; i < data.length; i++)
|
|
187
|
+
c = crcTable[(c ^ data[i]) & 0xff] ^ (c >>> 8);
|
|
188
|
+
return (c ^ 0xffffffff) >>> 0;
|
|
189
|
+
}
|
|
190
|
+
export function makeZip(files) {
|
|
191
|
+
const enc = new TextEncoder();
|
|
192
|
+
const parts = [], central = [];
|
|
193
|
+
let offset = 0;
|
|
194
|
+
for (const f of files) {
|
|
195
|
+
const name = enc.encode(f.name), data = typeof f.data === 'string' ? enc.encode(f.data) : f.data, crc = crc32(data);
|
|
196
|
+
const local = new Uint8Array(30 + name.length), l = new DataView(local.buffer);
|
|
197
|
+
l.setUint32(0, 0x04034b50, true);
|
|
198
|
+
l.setUint16(4, 20, true);
|
|
199
|
+
l.setUint16(6, 0x0800, true);
|
|
200
|
+
l.setUint16(8, 0, true);
|
|
201
|
+
l.setUint16(10, 0, true);
|
|
202
|
+
l.setUint16(12, 0x0021, true);
|
|
203
|
+
l.setUint32(14, crc, true);
|
|
204
|
+
l.setUint32(18, data.length, true);
|
|
205
|
+
l.setUint32(22, data.length, true);
|
|
206
|
+
l.setUint16(26, name.length, true);
|
|
207
|
+
l.setUint16(28, 0, true);
|
|
208
|
+
local.set(name, 30);
|
|
209
|
+
const cd = new Uint8Array(46 + name.length), c = new DataView(cd.buffer);
|
|
210
|
+
c.setUint32(0, 0x02014b50, true);
|
|
211
|
+
c.setUint16(4, 20, true);
|
|
212
|
+
c.setUint16(6, 20, true);
|
|
213
|
+
c.setUint16(8, 0x0800, true);
|
|
214
|
+
c.setUint16(10, 0, true);
|
|
215
|
+
c.setUint16(12, 0, true);
|
|
216
|
+
c.setUint16(14, 0x0021, true);
|
|
217
|
+
c.setUint32(16, crc, true);
|
|
218
|
+
c.setUint32(20, data.length, true);
|
|
219
|
+
c.setUint32(24, data.length, true);
|
|
220
|
+
c.setUint16(28, name.length, true);
|
|
221
|
+
c.setUint16(30, 0, true);
|
|
222
|
+
c.setUint16(32, 0, true);
|
|
223
|
+
c.setUint16(34, 0, true);
|
|
224
|
+
c.setUint16(36, 0, true);
|
|
225
|
+
c.setUint32(38, 0, true);
|
|
226
|
+
c.setUint32(42, offset, true);
|
|
227
|
+
cd.set(name, 46);
|
|
228
|
+
parts.push(local, data);
|
|
229
|
+
central.push(cd);
|
|
230
|
+
offset += local.length + data.length;
|
|
231
|
+
}
|
|
232
|
+
const cdSize = central.reduce((n, c) => n + c.length, 0);
|
|
233
|
+
const end = new Uint8Array(22), e = new DataView(end.buffer);
|
|
234
|
+
e.setUint32(0, 0x06054b50, true);
|
|
235
|
+
e.setUint16(4, 0, true);
|
|
236
|
+
e.setUint16(6, 0, true);
|
|
237
|
+
e.setUint16(8, files.length, true);
|
|
238
|
+
e.setUint16(10, files.length, true);
|
|
239
|
+
e.setUint32(12, cdSize, true);
|
|
240
|
+
e.setUint32(16, offset, true);
|
|
241
|
+
e.setUint16(20, 0, true);
|
|
242
|
+
const out = new Uint8Array(offset + cdSize + 22);
|
|
243
|
+
let p = 0;
|
|
244
|
+
for (const part of [...parts, ...central, end]) {
|
|
245
|
+
out.set(part, p);
|
|
246
|
+
p += part.length;
|
|
247
|
+
}
|
|
248
|
+
return out;
|
|
249
|
+
}
|
|
250
|
+
//# sourceMappingURL=figures.js.map
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ghostscript.ts — Ghostscript for dvisvgm, as a module of its own.
|
|
3
|
+
*
|
|
4
|
+
* dvisvgm interprets PostScript specials (PSTricks, EPS images, `ps:` code,
|
|
5
|
+
* graphicx's rotation and scaling under its dvips driver) through Ghostscript's
|
|
6
|
+
* C API. dvisvgm.wasm asks for "libgs" with dlopen, and src/c/gs-bridge.c
|
|
7
|
+
* answers with gsapi_* proxies that call Module.gsBridge, the object built
|
|
8
|
+
* here. It forwards each call to a separate Ghostscript wasm module
|
|
9
|
+
* (vendor/GHOSTSCRIPT.lock, served from dist/ghostscript/), copying buffers
|
|
10
|
+
* between the two heaps. Ghostscript is never linked into dvisvgm.wasm, and it
|
|
11
|
+
* is loaded only by a document whose DVI carries PostScript worth running
|
|
12
|
+
* (postscript.ts), only when the `ghostscript` bundle is enabled.
|
|
13
|
+
*
|
|
14
|
+
* Ghostscript opens files itself (an EPS goes in as `(fig.eps) run`), so for
|
|
15
|
+
* each dvisvgm run its /work and /texmf are mounted into the Ghostscript
|
|
16
|
+
* module with Emscripten's PROXYFS. One module serves every document in turn:
|
|
17
|
+
* gsapi_new_instance ... gsapi_delete_instance per dvisvgm run, never two at
|
|
18
|
+
* once (a non-threaded Ghostscript refuses a second live instance).
|
|
19
|
+
*/
|
|
20
|
+
/** The Emscripten module of the Ghostscript port (its gs.js), as far as it is used here. */
|
|
21
|
+
export interface GhostscriptModule {
|
|
22
|
+
HEAPU8: Uint8Array;
|
|
23
|
+
FS: any;
|
|
24
|
+
PROXYFS: any;
|
|
25
|
+
_malloc(n: number): number;
|
|
26
|
+
_free(p: number): void;
|
|
27
|
+
addFunction(fn: (...args: number[]) => number, sig: string): number;
|
|
28
|
+
stringToUTF8(s: string, ptr: number, max: number): void;
|
|
29
|
+
lengthBytesUTF8(s: string): number;
|
|
30
|
+
_gsapi_revision(p: number, len: number): number;
|
|
31
|
+
_gsapi_new_instance(pinstance: number, caller: number): number;
|
|
32
|
+
_gsapi_delete_instance(instance: number): void;
|
|
33
|
+
_gsapi_set_stdio(instance: number, stdin: number, stdout: number, stderr: number): number;
|
|
34
|
+
_gsapi_init_with_args(instance: number, argc: number, argv: number): number;
|
|
35
|
+
_gsapi_run_string_begin(instance: number, userErrors: number, pexit: number): number;
|
|
36
|
+
_gsapi_run_string_continue(instance: number, str: number, length: number, userErrors: number, pexit: number): number;
|
|
37
|
+
_gsapi_run_string_end(instance: number, userErrors: number, pexit: number): number;
|
|
38
|
+
_gsapi_exit(instance: number): number;
|
|
39
|
+
}
|
|
40
|
+
export type GhostscriptFactory = (moduleArg: Record<string, unknown>) => Promise<GhostscriptModule>;
|
|
41
|
+
/** What gs-bridge.js calls, on Module.gsBridge of a dvisvgm instance. */
|
|
42
|
+
export interface GsBridge {
|
|
43
|
+
available(): boolean;
|
|
44
|
+
revision(): [number, number];
|
|
45
|
+
newInstance(): number;
|
|
46
|
+
init(args: string[]): number;
|
|
47
|
+
run(op: 0 | 1 | 2, bytes: Uint8Array | null, userErrors: number): [number, number];
|
|
48
|
+
exit(): number;
|
|
49
|
+
deleteInstance(): void;
|
|
50
|
+
take(stream: 1 | 2, max: number): Uint8Array | null;
|
|
51
|
+
}
|
|
52
|
+
export declare class Ghostscript {
|
|
53
|
+
private readonly M;
|
|
54
|
+
private instance;
|
|
55
|
+
private readonly out;
|
|
56
|
+
private readonly cell;
|
|
57
|
+
private buf;
|
|
58
|
+
private bufSize;
|
|
59
|
+
private readonly fns;
|
|
60
|
+
private mounted;
|
|
61
|
+
/** set when the module trapped: it is not used again, and the engine loads a fresh one */
|
|
62
|
+
broken: boolean;
|
|
63
|
+
private constructor();
|
|
64
|
+
/** Instantiate the module from a compiled gs.wasm. */
|
|
65
|
+
static create(factory: GhostscriptFactory, wasm: WebAssembly.Module): Promise<Ghostscript>;
|
|
66
|
+
private view;
|
|
67
|
+
/** Give Ghostscript dvisvgm's files: its /work (the cwd) and its /texmf. */
|
|
68
|
+
mount(dvisvgmFS: any, dirs: string[]): void;
|
|
69
|
+
unmount(): void;
|
|
70
|
+
/** Run fn against the module; a trap marks it broken and becomes a Ghostscript fatal error. */
|
|
71
|
+
private guard;
|
|
72
|
+
bridge(): GsBridge;
|
|
73
|
+
}
|
|
74
|
+
export declare function compileGhostscript(key: string, bytes: () => Promise<Uint8Array>): Promise<WebAssembly.Module>;
|
|
75
|
+
/**
|
|
76
|
+
* A lazy loader for one engine: nothing is fetched until a document needs
|
|
77
|
+
* Ghostscript; then the glue is imported, gs.wasm compiled (once per process)
|
|
78
|
+
* and a module instantiated, which serves later documents too. A module that
|
|
79
|
+
* trapped is replaced. If loading fails, it says so once and returns null, so
|
|
80
|
+
* documents still render, with a warning, as without Ghostscript.
|
|
81
|
+
*/
|
|
82
|
+
export declare function ghostscriptLoader(o: {
|
|
83
|
+
glueUrl: string;
|
|
84
|
+
wasmUrl: string;
|
|
85
|
+
fetch: (url: string) => Promise<Uint8Array>;
|
|
86
|
+
factory?: GhostscriptFactory;
|
|
87
|
+
log?: (message: string) => void;
|
|
88
|
+
}): () => Promise<Ghostscript | null>;
|
|
89
|
+
/** Whether these options turn Ghostscript on: the option itself, or the `ghostscript` bundle in the list. */
|
|
90
|
+
export declare function ghostscriptWanted(ghostscript: boolean | undefined, bundles: (string | {
|
|
91
|
+
name: string;
|
|
92
|
+
})[]): boolean;
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ghostscript.ts — Ghostscript for dvisvgm, as a module of its own.
|
|
3
|
+
*
|
|
4
|
+
* dvisvgm interprets PostScript specials (PSTricks, EPS images, `ps:` code,
|
|
5
|
+
* graphicx's rotation and scaling under its dvips driver) through Ghostscript's
|
|
6
|
+
* C API. dvisvgm.wasm asks for "libgs" with dlopen, and src/c/gs-bridge.c
|
|
7
|
+
* answers with gsapi_* proxies that call Module.gsBridge, the object built
|
|
8
|
+
* here. It forwards each call to a separate Ghostscript wasm module
|
|
9
|
+
* (vendor/GHOSTSCRIPT.lock, served from dist/ghostscript/), copying buffers
|
|
10
|
+
* between the two heaps. Ghostscript is never linked into dvisvgm.wasm, and it
|
|
11
|
+
* is loaded only by a document whose DVI carries PostScript worth running
|
|
12
|
+
* (postscript.ts), only when the `ghostscript` bundle is enabled.
|
|
13
|
+
*
|
|
14
|
+
* Ghostscript opens files itself (an EPS goes in as `(fig.eps) run`), so for
|
|
15
|
+
* each dvisvgm run its /work and /texmf are mounted into the Ghostscript
|
|
16
|
+
* module with Emscripten's PROXYFS. One module serves every document in turn:
|
|
17
|
+
* gsapi_new_instance ... gsapi_delete_instance per dvisvgm run, never two at
|
|
18
|
+
* once (a non-threaded Ghostscript refuses a second live instance).
|
|
19
|
+
*/
|
|
20
|
+
/** gs_error_Fatal: what the proxies report when the module itself failed. */
|
|
21
|
+
const FATAL = -100;
|
|
22
|
+
export class Ghostscript {
|
|
23
|
+
M;
|
|
24
|
+
instance = 0;
|
|
25
|
+
out = [[], [], []]; // index 1 stdout, 2 stderr
|
|
26
|
+
cell; // 16 scratch bytes: instance pointer, exit code, revision
|
|
27
|
+
buf = 0;
|
|
28
|
+
bufSize = 0;
|
|
29
|
+
fns;
|
|
30
|
+
mounted = [];
|
|
31
|
+
/** set when the module trapped: it is not used again, and the engine loads a fresh one */
|
|
32
|
+
broken = false;
|
|
33
|
+
constructor(M) {
|
|
34
|
+
this.M = M;
|
|
35
|
+
this.cell = M._malloc(16);
|
|
36
|
+
const collect = (stream) => (_caller, ptr, len) => {
|
|
37
|
+
if (len > 0)
|
|
38
|
+
this.out[stream].push(this.M.HEAPU8.slice(ptr, ptr + len));
|
|
39
|
+
return len;
|
|
40
|
+
};
|
|
41
|
+
this.fns = [M.addFunction(() => 0, 'iiii'), M.addFunction(collect(1), 'iiii'), M.addFunction(collect(2), 'iiii')];
|
|
42
|
+
}
|
|
43
|
+
/** Instantiate the module from a compiled gs.wasm. */
|
|
44
|
+
static async create(factory, wasm) {
|
|
45
|
+
const M = await factory({
|
|
46
|
+
print: () => { }, printErr: () => { },
|
|
47
|
+
instantiateWasm(imports, receive) {
|
|
48
|
+
WebAssembly.instantiate(wasm, imports).then((inst) => receive(inst, wasm));
|
|
49
|
+
return {};
|
|
50
|
+
},
|
|
51
|
+
});
|
|
52
|
+
return new Ghostscript(M);
|
|
53
|
+
}
|
|
54
|
+
view() { return new DataView(this.M.HEAPU8.buffer); }
|
|
55
|
+
/** Give Ghostscript dvisvgm's files: its /work (the cwd) and its /texmf. */
|
|
56
|
+
mount(dvisvgmFS, dirs) {
|
|
57
|
+
const FS = this.M.FS;
|
|
58
|
+
for (const dir of dirs) {
|
|
59
|
+
if (!dvisvgmFS.analyzePath(dir).exists)
|
|
60
|
+
continue;
|
|
61
|
+
try {
|
|
62
|
+
FS.mkdirTree(dir);
|
|
63
|
+
}
|
|
64
|
+
catch { /* exists */ }
|
|
65
|
+
FS.mount(this.M.PROXYFS, { root: dir, fs: dvisvgmFS }, dir);
|
|
66
|
+
this.mounted.push(dir);
|
|
67
|
+
}
|
|
68
|
+
if (this.mounted.includes('/work'))
|
|
69
|
+
FS.chdir('/work');
|
|
70
|
+
}
|
|
71
|
+
unmount() {
|
|
72
|
+
const FS = this.M.FS;
|
|
73
|
+
try {
|
|
74
|
+
FS.chdir('/');
|
|
75
|
+
}
|
|
76
|
+
catch { /* ignore */ }
|
|
77
|
+
for (const dir of this.mounted.splice(0)) {
|
|
78
|
+
try {
|
|
79
|
+
FS.unmount(dir);
|
|
80
|
+
}
|
|
81
|
+
catch { /* ignore */ }
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
/** Run fn against the module; a trap marks it broken and becomes a Ghostscript fatal error. */
|
|
85
|
+
guard(fallback, fn) {
|
|
86
|
+
if (this.broken)
|
|
87
|
+
return fallback;
|
|
88
|
+
try {
|
|
89
|
+
return fn();
|
|
90
|
+
}
|
|
91
|
+
catch {
|
|
92
|
+
this.broken = true;
|
|
93
|
+
return fallback;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
bridge() {
|
|
97
|
+
const M = this.M;
|
|
98
|
+
return {
|
|
99
|
+
available: () => !this.broken,
|
|
100
|
+
revision: () => this.guard([0, 0], () => {
|
|
101
|
+
M._gsapi_revision(this.cell, 16); // wasm32: product, copyright (pointers), revision, revisiondate (longs)
|
|
102
|
+
const v = this.view();
|
|
103
|
+
return [v.getInt32(this.cell + 8, true), v.getInt32(this.cell + 12, true)];
|
|
104
|
+
}),
|
|
105
|
+
newInstance: () => this.guard(FATAL, () => {
|
|
106
|
+
this.out[1] = [];
|
|
107
|
+
this.out[2] = [];
|
|
108
|
+
// *pinstance must be null: a non-null value asks to share that (existing) instance
|
|
109
|
+
this.view().setUint32(this.cell, 0, true);
|
|
110
|
+
const code = M._gsapi_new_instance(this.cell, 0);
|
|
111
|
+
if (code < 0)
|
|
112
|
+
return code;
|
|
113
|
+
this.instance = this.view().getUint32(this.cell, true);
|
|
114
|
+
return M._gsapi_set_stdio(this.instance, ...this.fns);
|
|
115
|
+
}),
|
|
116
|
+
init: (args) => this.guard(FATAL, () => {
|
|
117
|
+
const enc = new TextEncoder();
|
|
118
|
+
const strs = args.map((a) => enc.encode(a));
|
|
119
|
+
const block = M._malloc(4 * strs.length + strs.reduce((n, s) => n + s.length + 1, 0));
|
|
120
|
+
let p = block + 4 * strs.length;
|
|
121
|
+
strs.forEach((s, i) => {
|
|
122
|
+
this.view().setUint32(block + 4 * i, p, true);
|
|
123
|
+
M.HEAPU8.set(s, p);
|
|
124
|
+
M.HEAPU8[p + s.length] = 0;
|
|
125
|
+
p += s.length + 1;
|
|
126
|
+
});
|
|
127
|
+
try {
|
|
128
|
+
return M._gsapi_init_with_args(this.instance, strs.length, block);
|
|
129
|
+
}
|
|
130
|
+
finally {
|
|
131
|
+
M._free(block);
|
|
132
|
+
}
|
|
133
|
+
}),
|
|
134
|
+
run: (op, bytes, userErrors) => this.guard([FATAL, FATAL], () => {
|
|
135
|
+
const exit = this.cell + 4;
|
|
136
|
+
let code;
|
|
137
|
+
if (op === 0)
|
|
138
|
+
code = M._gsapi_run_string_begin(this.instance, userErrors, exit);
|
|
139
|
+
else if (op === 2)
|
|
140
|
+
code = M._gsapi_run_string_end(this.instance, userErrors, exit);
|
|
141
|
+
else {
|
|
142
|
+
const n = bytes.length;
|
|
143
|
+
if (n > this.bufSize) {
|
|
144
|
+
if (this.buf)
|
|
145
|
+
M._free(this.buf);
|
|
146
|
+
this.buf = M._malloc(n);
|
|
147
|
+
this.bufSize = n;
|
|
148
|
+
}
|
|
149
|
+
M.HEAPU8.set(bytes, this.buf);
|
|
150
|
+
code = M._gsapi_run_string_continue(this.instance, this.buf, n, userErrors, exit);
|
|
151
|
+
}
|
|
152
|
+
return [code, this.view().getInt32(exit, true)];
|
|
153
|
+
}),
|
|
154
|
+
exit: () => this.guard(FATAL, () => M._gsapi_exit(this.instance)),
|
|
155
|
+
deleteInstance: () => this.guard(undefined, () => {
|
|
156
|
+
if (this.instance)
|
|
157
|
+
M._gsapi_delete_instance(this.instance);
|
|
158
|
+
this.instance = 0;
|
|
159
|
+
}),
|
|
160
|
+
take: (stream, max) => {
|
|
161
|
+
const q = this.out[stream];
|
|
162
|
+
if (!q.length)
|
|
163
|
+
return null;
|
|
164
|
+
const head = q[0];
|
|
165
|
+
if (head.length <= max) {
|
|
166
|
+
q.shift();
|
|
167
|
+
return head;
|
|
168
|
+
}
|
|
169
|
+
q[0] = head.subarray(max);
|
|
170
|
+
return head.subarray(0, max);
|
|
171
|
+
},
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
/** One compile of gs.wasm per process, whoever asks. */
|
|
176
|
+
const compiled = new Map();
|
|
177
|
+
export function compileGhostscript(key, bytes) {
|
|
178
|
+
let p = compiled.get(key);
|
|
179
|
+
if (!p) {
|
|
180
|
+
p = bytes().then((b) => WebAssembly.compile(b));
|
|
181
|
+
p.catch(() => compiled.delete(key));
|
|
182
|
+
compiled.set(key, p);
|
|
183
|
+
}
|
|
184
|
+
return p;
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* A lazy loader for one engine: nothing is fetched until a document needs
|
|
188
|
+
* Ghostscript; then the glue is imported, gs.wasm compiled (once per process)
|
|
189
|
+
* and a module instantiated, which serves later documents too. A module that
|
|
190
|
+
* trapped is replaced. If loading fails, it says so once and returns null, so
|
|
191
|
+
* documents still render, with a warning, as without Ghostscript.
|
|
192
|
+
*/
|
|
193
|
+
export function ghostscriptLoader(o) {
|
|
194
|
+
let current = null;
|
|
195
|
+
let failed = false;
|
|
196
|
+
return async () => {
|
|
197
|
+
if (failed)
|
|
198
|
+
return null;
|
|
199
|
+
if (current && !current.broken)
|
|
200
|
+
return current;
|
|
201
|
+
try {
|
|
202
|
+
const factory = o.factory ?? (await import(/* @vite-ignore */ o.glueUrl)).default;
|
|
203
|
+
const wasm = await compileGhostscript(o.wasmUrl, () => o.fetch(o.wasmUrl));
|
|
204
|
+
current = await Ghostscript.create(factory, wasm);
|
|
205
|
+
return current;
|
|
206
|
+
}
|
|
207
|
+
catch (e) {
|
|
208
|
+
failed = true;
|
|
209
|
+
o.log?.(`Ghostscript could not be loaded from ${o.wasmUrl}: ${e?.message ?? e}`);
|
|
210
|
+
return null;
|
|
211
|
+
}
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
/** Whether these options turn Ghostscript on: the option itself, or the `ghostscript` bundle in the list. */
|
|
215
|
+
export function ghostscriptWanted(ghostscript, bundles) {
|
|
216
|
+
return ghostscript ?? bundles.some((b) => (typeof b === 'string' ? b : b.name) === 'ghostscript');
|
|
217
|
+
}
|
|
218
|
+
//# sourceMappingURL=ghostscript.js.map
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* mp-tikz-wasm — public API (docs/08). `MetaPost.create()` starts a Web
|
|
3
|
+
* Worker in browsers and runs in-process in Node (or when `worker: false`).
|
|
4
|
+
*/
|
|
5
|
+
import type { MetaPostOptions, RunOptions, RunResult, ProgressEvent, BundleName, LatexRunOptions, LatexResult, PrefetchKind, LogLevel, LogRecord, BibtexOptions, MakeindexOptions, ToolResult } from './types.js';
|
|
6
|
+
export * from './types.js';
|
|
7
|
+
export { sanitizeSvg, postProcessSvg } from './render/svg.js';
|
|
8
|
+
export { scanTexBlocks, scanInputs } from './tex/scanner.js';
|
|
9
|
+
export { parseMetaPostLog } from './diagnostics.js';
|
|
10
|
+
export { splitMpx } from './tex/mpx.js';
|
|
11
|
+
export { LOG_LEVELS, formatRecord, consoleSink } from './logger.js';
|
|
12
|
+
export { DEFAULT_BUNDLES } from './bundles-config.js';
|
|
13
|
+
export { figureHash, figureName, figureDocument, extractFigures, renderFigure, wrapTikz, wrapMetaPost, makeZip, FIGURE_FILE } from './figures.js';
|
|
14
|
+
export type { FigureKind, FigureRequest, FigureResult, SavedFigure, FoundFigure } from './figures.js';
|
|
15
|
+
export declare class MetaPost {
|
|
16
|
+
private backend;
|
|
17
|
+
private options;
|
|
18
|
+
private listeners;
|
|
19
|
+
private queue;
|
|
20
|
+
private readonly t0;
|
|
21
|
+
readonly version: {
|
|
22
|
+
metapost: string;
|
|
23
|
+
tex: string;
|
|
24
|
+
build: string;
|
|
25
|
+
};
|
|
26
|
+
private constructor();
|
|
27
|
+
static create(options?: MetaPostOptions): Promise<MetaPost>;
|
|
28
|
+
/**
|
|
29
|
+
* The stall watchdog (worker only): the run is killed when `timeoutMs` pass without a progress
|
|
30
|
+
* event or a line of engine output. Every engine phase and every on-demand file fetch is progress,
|
|
31
|
+
* so a first run on a slow host that spends a minute fetching files one after another is left
|
|
32
|
+
* alone, and so is a long TeX run that is still printing; a silent, hung engine is not.
|
|
33
|
+
*/
|
|
34
|
+
private watchdog;
|
|
35
|
+
/** Events from either backend: log records reach the console (or the `logger` option), raw lines the `log` option, then any listeners. */
|
|
36
|
+
private handleEvent;
|
|
37
|
+
/** A record from this side of the worker boundary (a failed call, the watchdog), subject to the same level. */
|
|
38
|
+
private hostRecord;
|
|
39
|
+
private failing;
|
|
40
|
+
/** How much reaches the console (see `MetaPostOptions.logLevel`); can be changed at any time, also mid-run. */
|
|
41
|
+
get logLevel(): LogLevel;
|
|
42
|
+
set logLevel(level: LogLevel);
|
|
43
|
+
/** Compile MetaPost source. Calls are serialised on this instance. */
|
|
44
|
+
run(source: string, options?: RunOptions): Promise<RunResult>;
|
|
45
|
+
/** Typeset a whole LaTeX/TikZ (or plain TeX) document to one SVG per page. */
|
|
46
|
+
latex(source: string, options?: LatexRunOptions): Promise<LatexResult>;
|
|
47
|
+
/**
|
|
48
|
+
* BibTeX on the .aux a latex() run wrote (`files: result.artifacts`, plus the .bib databases):
|
|
49
|
+
* doc.bbl and doc.blg come back in `artifacts`, for the next latex() run. TeX Live's styles
|
|
50
|
+
* are in the `latex-doc` bundle. Serialised with run() and latex() on this instance.
|
|
51
|
+
*/
|
|
52
|
+
bibtex(options?: BibtexOptions): Promise<ToolResult>;
|
|
53
|
+
/** makeindex on the .idx a latex() run wrote: doc.ind and doc.ilg come back in `artifacts`. */
|
|
54
|
+
makeindex(options?: MakeindexOptions): Promise<ToolResult>;
|
|
55
|
+
private serialise;
|
|
56
|
+
addFiles(files: Record<string, string | Uint8Array>): Promise<void>;
|
|
57
|
+
/**
|
|
58
|
+
* Take off this instance files that addFiles or a run's `files` put there, named as they were
|
|
59
|
+
* given (all of them with no names). Passed files otherwise stay for the instance's lifetime:
|
|
60
|
+
* a host typesetting several documents should remove one document's .aux, .toc, .bbl before
|
|
61
|
+
* the next, or the next one reads them.
|
|
62
|
+
*/
|
|
63
|
+
removeFiles(names?: string[]): Promise<void>;
|
|
64
|
+
preload(bundles: BundleName[]): Promise<void>;
|
|
65
|
+
/** Fetch in parallel the files a first run of these kinds needs (see `MetaPostOptions.prefetch`). Resolves to the number fetched. */
|
|
66
|
+
prefetch(kinds: PrefetchKind[]): Promise<number>;
|
|
67
|
+
clearCache(): Promise<void>;
|
|
68
|
+
on(event: 'progress', fn: (e: ProgressEvent) => void): () => void;
|
|
69
|
+
on(event: 'log', fn: (line: string) => void): () => void;
|
|
70
|
+
on(event: 'record', fn: (r: LogRecord) => void): () => void;
|
|
71
|
+
private emit;
|
|
72
|
+
dispose(): void;
|
|
73
|
+
}
|
|
74
|
+
/** A pool of independent instances for batch work (docs/10 §2.3). */
|
|
75
|
+
export declare class MetaPostPool {
|
|
76
|
+
private all;
|
|
77
|
+
private idle;
|
|
78
|
+
private waiters;
|
|
79
|
+
private constructor();
|
|
80
|
+
static create(options?: MetaPostOptions & {
|
|
81
|
+
size?: number;
|
|
82
|
+
}): Promise<MetaPostPool>;
|
|
83
|
+
private acquire;
|
|
84
|
+
private release;
|
|
85
|
+
run(source: string, options?: RunOptions): Promise<RunResult>;
|
|
86
|
+
dispose(): void;
|
|
87
|
+
}
|