Oregami
Repositories/oxedyne/daimond

oxedyne/daimond/dev/probe_typstsvg.mjs

3.3 KiB, 1 run

created by r2519314175:111, which is this file's identity for as long as the history lasts, whatever it is later renamed to

download · who wrote it · its history

1// probe_typstsvg.mjs — what the renderer actually hands back, whole against window.
2//
3// The author reported ghosted/double-struck glyphs on every line and solid black
4// bars where the contents entries and three inline spans belong. Both are the shape
5// of typst.ts's INVISIBLE TEXT LAYER becoming visible: it emits real `<text>` on top
6// of the glyph outlines so a reader can select and search, and the only thing that
7// makes it invisible is a rule in the stylesheet it emits beside it. So this asks
8// the renderer for the same document twice — whole (`svg_data`) and windowed
9// (`render_in_window`) — and reports what each one carries.
10//
11// It asserts nothing and is not a verifier.
12//
13// eval "$(bash dev/world.sh 5 --env)"
14// node dev/probe_typstsvg.mjs
15import fs from 'node:fs';
16import { open, scratch } from './harness.mjs';
17
18const WORK = scratch('typstsvg');
19fs.mkdirSync(WORK, { recursive: true });
20
21const DOC = `#set page(width: 400pt, height: 300pt, margin: 24pt)
22#set text(size: 11pt)
23= A heading
24#outline()
25Some ordinary body text, long enough to wrap onto a second line so that a
26double-struck run would be obvious to anybody looking at it.
27Created using #link("https://typst.app")[Typst] and #link("https://rust-lang.org")[Rust].
28== Another heading
29More text under it.
30`;
31
32const s = await open({ name: 'typstsvg' });
33const p = s.page;
34await p.waitForTimeout(1200);
35
36const r = await p.evaluate(async (src) => {
37 const m = await import('/pkg/oxedyne_daimond.js');
38 m.set_locked_packs('');
39 await m.write_file('svgprobe/main.typ', src);
40 const out = await m.typst_compile_project_vector('svgprobe/main.typ');
41 if (!out.vector) return { error: String(out.error || 'nothing').slice(0, 400) };
42 const g = await import('/vendor/typst/typst_ts_renderer.mjs');
43 await g.default('/vendor/typst/typst_ts_renderer_bg.wasm');
44 const rr = await new g.TypstRendererBuilder().build();
45 const ses = rr.session_from_artifact(out.vector, 'vector');
46 const info = ses.pages_info;
47 const whole = rr.svg_data(ses);
48 const ses2 = rr.session_from_artifact(out.vector, 'vector');
49 const win = ses2.render_in_window(0, 0, info.width(), info.height());
50 ses.free(); ses2.free();
51 return { whole, win, w: info.width(), h: info.height(), pages: info.page_count };
52}, DOC);
53
54if (r.error) { console.log('REFUSED: ' + r.error); await s.close(); process.exit(1); }
55
56const look = (name, svg) => {
57 fs.writeFileSync(`${WORK}/${name}.svg`, svg);
58 const styles = svg.match(/<style[\s\S]*?<\/style>/g) || [];
59 console.log(`\n=== ${name} — ${(svg.length / 1024).toFixed(1)} KB ===`);
60 console.log(` <style> blocks: ${styles.length}`);
61 for (const st of styles) console.log(' ' + st.replace(/\s+/g, ' ').slice(0, 300));
62 for (const k of ['tsel', 'typst-text', 'typst-doc', '<text', 'class="typst', 'data-tid',
63 '<script', 'pseudo-link', 'typst-link', 'fill="#000000"', 'fill: none']) {
64 const n = svg.split(k).length - 1;
65 if (n) console.log(` ${JSON.stringify(k)} x${n}`);
66 }
67 const t = svg.match(/<text[^>]*>[\s\S]{0,80}/);
68 if (t) console.log(' first <text>: ' + t[0].replace(/\s+/g, ' ').slice(0, 240));
69 console.log(' head: ' + svg.slice(0, 400).replace(/\s+/g, ' '));
70};
71
72console.log(`document ${r.w.toFixed(1)} x ${r.h.toFixed(1)} pt, ${r.pages} page(s)`);
73look('whole', r.whole);
74look('window', r.win);
75console.log(`\nwrote ${WORK}/whole.svg and ${WORK}/window.svg`);
76await s.close();