retex_engine/lib.rs
1//! The retex TeX engine: a clean-room implementation of LuaTeX's
2//! typesetting machine.
3//!
4//! The [`Engine`] holds all state of a TeX run. Its methods are spread
5//! over modules following the parts of *TeX: The Program*:
6//!
7//! * [`print`] and [`error`] — terminal and log output, error recovery;
8//! * [`hash`], [`eqtb`], [`save`] — control sequences, equivalents, grouping;
9//! * [`input`] — the input stack and the tokenizer (`get_next`);
10//! * [`expand`], [`cond`], [`scan`] — expansion, conditionals, scanning;
11//! * [`prefixed`] — assignments;
12//! * [`maincontrol`] — the chief executive.
13
14#![forbid(unsafe_code)]
15#![allow(clippy::too_many_lines)]
16
17pub mod align;
18pub mod backend;
19mod boxnote;
20pub mod build;
21pub mod clock;
22pub mod cmd;
23mod cmd_gen;
24pub mod codes;
25pub mod cond;
26pub mod describe;
27pub mod diag;
28pub mod dirs;
29pub mod display;
30pub mod dvi;
31pub mod eqtb;
32pub mod error;
33pub mod expand;
34pub mod expansion;
35mod figfile;
36pub mod figures;
37pub mod files;
38pub mod fingerprint;
39pub mod fonts;
40pub mod format;
41pub mod glue;
42pub mod hash;
43pub mod hmode;
44pub mod hyph;
45pub mod image;
46pub mod init;
47pub mod input;
48pub mod linebreak;
49pub mod maincontrol;
50pub mod math;
51pub mod mathdisplay;
52pub mod mathparams;
53pub mod meaning;
54pub mod misc;
55pub mod mlist;
56pub mod native;
57pub mod nodeproc;
58pub mod nodes;
59pub mod pack;
60pub mod page;
61pub mod pagenote;
62pub mod pdfext;
63pub(crate) mod pdfship;
64pub mod prefixed;
65pub mod primitives;
66pub mod primitives_gen;
67pub mod print;
68pub mod profile;
69pub mod protrusion;
70pub mod save;
71pub mod scan;
72pub mod shipout;
73pub mod showbox;
74pub mod snapshot;
75pub mod synctex;
76pub mod the;
77pub mod timing;
78pub mod token;
79pub mod typeset;
80pub mod utf8x;
81
82use cmd::Cmd;
83use cond::CondState;
84use eqtb::Eqtb;
85use error::ErrorState;
86use files::FileState;
87use hash::{CsId, CsTable};
88use input::InputState;
89use print::Printer;
90use save::SaveStack;
91use token::Token;
92
93/// Callbacks from the engine into an embedded Lua interpreter.
94///
95/// The engine does not depend on Lua. `\directlua` and related primitives
96/// call this trait, which `retex-lua` implements. The host is shared
97/// (`Rc`), so nested calls (Lua → TeX → Lua) reach it too.
98pub trait LuaHost {
99 /// Runs a chunk of Lua code for `\directlua` (or `\latelua`, etc.).
100 /// `name` is the chunk name. Text printed with `tex.print` must be
101 /// pushed back into the engine's input by the host.
102 fn run(&self, engine: &mut Engine, name: &str, code: &[u8]) -> Result<(), String>;
103
104 /// Calls the registered Lua function `n` (`\luafunction n`) with its
105 /// number, like [`run`](Self::run) runs a chunk; `None` if there is
106 /// no such function.
107 fn call_function(&self, _engine: &mut Engine, _n: i32) -> Option<Result<(), String>> {
108 None
109 }
110
111 /// Sets entries of expl3's intarrays (l3intarray under LuaTeX keeps
112 /// them in Lua tables), as `\__intarray_gset:w` does: for each
113 /// `(array, first, values)`, entry `first + k` of the array numbered
114 /// `array` (`\__intarray:w <array>`) gets `values[k]`. `gset` is the
115 /// `\luafunction` number of `\__intarray_gset:w`, through which the
116 /// arrays are found. `false`, with nothing changed, when the arrays
117 /// are not reachable natively.
118 fn intarray_set(&self, _gset: i32, _writes: &[(i64, i64, &[i64])]) -> bool {
119 false
120 }
121
122 /// Data to store in the format file (e.g. `lua.bytecode`).
123 fn dump(&self, _engine: &mut Engine) -> Vec<u8> {
124 Vec::new()
125 }
126
127 /// Restores data stored by [`dump`](Self::dump).
128 fn undump(&self, _engine: &mut Engine, _data: &[u8]) -> Result<(), String> {
129 Ok(())
130 }
131
132 /// Calls callback `name` without arguments, ignoring its results
133 /// (`pre_dump`, `stop_run`, …). Does nothing if none is registered.
134 fn call_simple(&self, _engine: &mut Engine, _name: &str) -> Result<(), String> {
135 Ok(())
136 }
137
138 /// Calls callback `name` with one string argument, ignoring its
139 /// results (`buildpage_filter`, `contribute_filter`).
140 fn call_with_string(
141 &self,
142 _engine: &mut Engine,
143 _name: &str,
144 _arg: &str,
145 ) -> Result<(), String> {
146 Ok(())
147 }
148
149 /// Whether a Lua function is registered for callback `name`.
150 fn has_callback(&self, _name: &str) -> bool {
151 false
152 }
153
154 /// Whether callback `name` runs Lua code of a package's own, not only
155 /// retex's native luaotfload (whose results depend on the fonts
156 /// only): for the figure cache.
157 fn foreign_callback(&self, name: &str) -> bool {
158 self.has_callback(name)
159 }
160
161 /// Calls the `define_font` callback. Returns the id of the font it
162 /// defined, or `None` if the font could not be loaded.
163 fn define_font(&self, _engine: &mut Engine, _name: &[u8], _size: i32) -> Option<u32> {
164 None
165 }
166
167 /// Calls a node-list filter callback (`pre_linebreak_filter`,
168 /// `hpack_filter`, `post_linebreak_filter`, …) on the list starting at
169 /// `head`. Returns the new head; [`ListFilter::Flush`] means the list
170 /// must be discarded.
171 fn list_filter(
172 &self,
173 _engine: &mut Engine,
174 _callback: &str,
175 head: nodes::NodeRef,
176 _group: &str,
177 ) -> ListFilter {
178 ListFilter::Keep(head)
179 }
180
181 /// Calls the `hpack_quality` or `vpack_quality` callback `cb` for box
182 /// `b` with `incident` (overfull, underfull, loose, tight) and its
183 /// detail; returns the node the callback gave (null if none).
184 #[allow(clippy::too_many_arguments)]
185 fn pack_quality(
186 &self,
187 _engine: &mut Engine,
188 _cb: &str,
189 _incident: &str,
190 _detail: i32,
191 _b: nodes::NodeRef,
192 _first: i32,
193 _last: i32,
194 ) -> nodes::NodeRef {
195 nodes::NodeRef::NULL
196 }
197
198 /// Runs the `hyphenate`, `ligaturing` or `kerning` callback `cb` on
199 /// the list after `head` (a temporary node), with its last node:
200 /// `true` if a function or `false` is registered, which replaces the
201 /// built-in pass.
202 fn node_pass(&self, _engine: &mut Engine, _cb: &str, _head: nodes::NodeRef) -> bool {
203 false
204 }
205
206 /// Calls the `linebreak_filter` callback on paragraph `head`
207 /// (`display`: a display follows): `Some(list)` if it returned a node
208 /// list, which replaces line breaking; `None` otherwise.
209 fn linebreak_filter(
210 &self,
211 _engine: &mut Engine,
212 _head: nodes::NodeRef,
213 _display: bool,
214 ) -> Option<nodes::NodeRef> {
215 None
216 }
217
218 /// Calls the `append_to_vlist_filter` callback for box `b`, about to be
219 /// appended to a vertical list at `location` (`box`, `post_linebreak`,
220 /// `equation`, `equation_number`, `alignment`) after `prev_depth`:
221 /// `None` if none is set; else the list to append instead (null for
222 /// none) and the new `\prevdepth`, if it gave one.
223 fn append_to_vlist_filter(
224 &self,
225 _engine: &mut Engine,
226 _b: nodes::NodeRef,
227 _location: &str,
228 _prev_depth: i32,
229 ) -> Option<(nodes::NodeRef, Option<i32>)> {
230 None
231 }
232
233 /// Calls the `mlist_to_hlist` callback on math list `head` in style
234 /// `style` (0–7); `None` if the callback failed or none is set.
235 fn mlist_to_hlist(
236 &self,
237 _engine: &mut Engine,
238 _head: nodes::NodeRef,
239 _style: i32,
240 _penalties: bool,
241 ) -> Option<nodes::NodeRef> {
242 None
243 }
244
245 /// Runs shell command `cmd` under the shell escape rules (as Lua's
246 /// `io.popen` does) and returns its output; `None` if it may not run.
247 fn pipe_read(&self, _engine: &mut Engine, _cmd: &[u8]) -> Option<Vec<u8>> {
248 None
249 }
250
251 /// Calls the `input_level_string` callback with input-stack depth `n`;
252 /// `None` if it returned no string.
253 fn input_level_string(&self, _engine: &mut Engine, _n: i64) -> Option<Vec<u8>> {
254 None
255 }
256
257 /// Calls the `glyph_info` callback for glyph node `p`: `Some(text)` is
258 /// what to print, `None` means the callback returned no string.
259 fn glyph_info(&self, _engine: &mut Engine, _p: nodes::NodeRef) -> Option<Vec<u8>> {
260 None
261 }
262
263 /// Runs the Lua value kept under reference number `reference` (see
264 /// [`nodes::LuaRefs`]) at shipout: a function is called with `args`,
265 /// a string is run as Lua code. For the `lua` commands of virtual
266 /// characters and `\latelua` functions.
267 fn call_reference(
268 &self,
269 _engine: &mut Engine,
270 _reference: i32,
271 _args: ReferenceArgs,
272 ) -> Result<(), String> {
273 Ok(())
274 }
275}
276
277/// The arguments of a Lua function called at shipout (see
278/// [`LuaHost::call_reference`]).
279#[derive(Clone, Copy, Debug, PartialEq, Eq)]
280pub enum ReferenceArgs {
281 /// A virtual character's `lua` command: the font id and the
282 /// character.
283 Glyph(u32, u32),
284 /// A `\latelua` function: the whatsit (as a node userdata).
285 Node(nodes::NodeRef),
286}
287
288/// Result of a node-list filter callback.
289#[derive(Clone, Copy, Debug, PartialEq, Eq)]
290pub enum ListFilter {
291 /// Continue with this list (possibly the original head).
292 Keep(nodes::NodeRef),
293 /// The callback returned `false`: discard the list.
294 Flush,
295}
296
297impl Engine {
298 /// Whether the Lua callback `name` is set.
299 pub fn has_callback(&self, name: &str) -> bool {
300 self.lua.as_ref().is_some_and(|h| h.has_callback(name))
301 }
302
303 /// Calls the notification callback `name` (`buildpage_filter`,
304 /// `contribute_filter`) with `info`, if one is set.
305 pub fn notify_callback(&mut self, name: &str, info: &str) {
306 if let Some(h) = self.lua.clone()
307 && h.has_callback(name)
308 && let Err(e) = h.call_with_string(self, name, info)
309 {
310 self.lua_error(&e);
311 }
312 }
313
314 /// Calls `buildpage_filter` with `info` (where the page builder was
315 /// called from, observed), then builds the page.
316 pub fn build_page_from(&mut self, info: &str) {
317 self.notify_callback("buildpage_filter", info);
318 self.build_page();
319 }
320
321 /// Runs a node-list filter callback if one is registered (the hook
322 /// typesetting code calls at LuaTeX's callback points).
323 pub fn run_list_filter(
324 &mut self,
325 callback: &str,
326 head: nodes::NodeRef,
327 group: &str,
328 ) -> ListFilter {
329 match self.lua.clone() {
330 Some(h) if h.has_callback(callback) => {
331 self.figure_callback(callback);
332 h.list_filter(self, callback, head, group)
333 }
334 _ => ListFilter::Keep(head),
335 }
336 }
337}
338
339impl Engine {
340 /// Converts a finished formula's math list into an hlist, through the
341 /// `mlist_to_hlist` callback when one is set (LuaTeX calls it for the
342 /// outermost list of each formula and equation number; observed: not
343 /// for an empty one).
344 pub fn formula_to_hlist(
345 &mut self,
346 head: nodes::NodeRef,
347 style: i32,
348 penalties: bool,
349 ) -> nodes::NodeRef {
350 if head.is_null() {
351 return nodes::NodeRef::NULL;
352 }
353 if let Some(h) = self.lua.clone()
354 && h.has_callback("mlist_to_hlist")
355 {
356 self.figure_callback("mlist_to_hlist");
357 if let Some(r) = h.mlist_to_hlist(self, head, style, penalties) {
358 if !r.is_null() {
359 self.nodes.set_prev(r, nodes::NodeRef::NULL);
360 }
361 return r;
362 }
363 return nodes::NodeRef::NULL;
364 }
365 self.mlist_to_hlist(head, style, penalties)
366 }
367
368 /// Prints the input level before a line of macro tracing through the
369 /// `input_level_string` callback (LaTeX's `\tracingstacklevels`);
370 /// returns false if no callback is set. A newline in the string ends
371 /// the line.
372 pub fn print_input_level(&mut self) -> bool {
373 let Some(h) = self.lua.clone() else {
374 return false;
375 };
376 if !h.has_callback("input_level_string") {
377 return false;
378 }
379 let n = self.input.stack.len().saturating_sub(1) as i64;
380 if let Some(s) = h.input_level_string(self, n) {
381 for (i, part) in s.split(|&b| b == b'\n').enumerate() {
382 if i > 0 {
383 self.printer.print_ln();
384 }
385 self.printer.print_bytes(part);
386 }
387 }
388 true
389 }
390
391 /// Runs the `glyph_info` callback for glyph `p` when one is set:
392 /// `None` without a callback, `Some(None)` when it returned no string.
393 pub fn run_glyph_info(&mut self, p: nodes::NodeRef) -> Option<Option<Vec<u8>>> {
394 match self.lua.clone() {
395 Some(h) if h.has_callback("glyph_info") => Some(h.glyph_info(self, p)),
396 _ => None,
397 }
398 }
399}
400
401/// A one-shot callback into the host, run with the engine borrowed.
402pub type EngineHook = Box<dyn FnOnce(&mut Engine)>;
403
404/// Shows diagnostics before the terminal is read (see
405/// [`Engine::prompt_hook`]).
406pub type PromptHook = Box<dyn FnMut(&[diag::Diagnostic], &str) -> bool>;
407
408/// The global state of a TeX run.
409pub struct Engine {
410 /// Control-sequence names.
411 pub hash: CsTable,
412 /// The table of equivalents.
413 pub eqtb: Eqtb,
414 /// The save stack and grouping state.
415 pub save: SaveStack,
416 /// The input stack.
417 pub input: InputState,
418 /// Terminal, log and `\write` output.
419 pub printer: Printer,
420 /// Error-handling state.
421 pub err: ErrorState,
422 /// Conditionals.
423 pub cond: CondState,
424 /// Files: `\openin`/`\read`, `\openout`, input file stack.
425 pub files: FileState,
426 /// The file finder.
427 pub kpse: Option<retex_kpath::Shared>,
428 /// The Lua interpreter, when available.
429 pub lua: Option<std::rc::Rc<dyn LuaHost>>,
430
431 /// The current command code (`cur_cmd`).
432 pub cur_cmd: Cmd,
433 /// The current modifier (`cur_chr`).
434 pub cur_chr: u32,
435 /// The current control sequence (`cur_cs`); [`hash::NO_CS`] for
436 /// characters.
437 pub cur_cs: CsId,
438 /// The current token (`cur_tok`).
439 pub cur_tok: Token,
440
441 /// The value most recently scanned (`cur_val`).
442 pub cur_val: i32,
443 /// The glue most recently scanned, when `cur_val_level` is glue.
444 pub cur_glue: glue::Glue,
445 /// The token list most recently scanned, when `cur_val_level` is tokens.
446 pub cur_toks: Option<token::TokenList>,
447 /// The level of `cur_val`.
448 pub cur_val_level: scan::ValLevel,
449
450 /// Well-known control sequences.
451 pub cs: init::KnownCs,
452 /// Whether the run is in IniTeX mode.
453 pub ini_version: bool,
454 /// Shell escape mode (see [`init::Options::shell_escape`]).
455 pub shell_escape: u8,
456 /// The format identification printed in the banner.
457 pub format_ident: String,
458 /// The job name, once known.
459 pub job_name: Option<Vec<u8>>,
460 /// Token saved by `\afterassignment` (`after_token`).
461 pub after_token: Token,
462 /// Number of nested expansions (LuaTeX's `expand_depth_count`).
463 pub expand_depth: u32,
464 /// The current mode (copy of the innermost nest level's mode).
465 pub mode: maincontrol::Mode,
466 /// The mode last shown by `show_cur_cmd_chr`.
467 pub shown_mode: maincontrol::Mode,
468 /// The format name (`\formatname`).
469 pub format_name: String,
470 /// Math as LuaTeX 1.24 (TeX Live 2026) typesets it, chosen for formats
471 /// of LaTeX 2026-06 and later (see [`Engine::load_format`]); else
472 /// LuaTeX 1.22 (TeX Live 2025), the version retex reports.
473 pub math_124: bool,
474 /// Building an operator's character nucleus, which gets no italic
475 /// correction in OpenType math (LuaTeX 1.24 as 1.22).
476 pub math_op_nucleus: bool,
477 /// Node memory.
478 pub nodes: nodes::Nodes,
479 /// The semantic nest; the last entry is the current list.
480 pub nest: Vec<maincontrol::ListState>,
481 /// Loaded fonts.
482 pub fonts: fonts::FontTable,
483 /// The `long_state` of the macro being called.
484 pub long_state: Cmd,
485 /// The control sequence being defined (for diagnostics).
486 pub def_ref_cs: CsId,
487 /// The partial token list printed by `runaway`.
488 pub runaway_list: Vec<Token>,
489 /// The control sequence last made by `\csname` (`\lastnamedcs`).
490 pub last_cs_name: CsId,
491 /// Frozen copies made by `\primitive`.
492 pub frozen_prims: std::collections::HashMap<Vec<u8>, CsId>,
493 /// Mark texts.
494 pub marks: typeset::Marks,
495 /// Set when a `\read` line has been consumed.
496 pub input_read_finished: bool,
497 /// Whether the long `\errmessage` help has been shown.
498 pub long_help_seen: bool,
499 /// `\deadcycles`.
500 pub dead_cycles: i32,
501 /// `\insertpenalties`.
502 pub insert_penalties: i32,
503 /// `\badness`.
504 pub last_badness: i32,
505 /// Token lists show a macro parameter character once, not doubled
506 /// (`\luaescapestring`).
507 pub single_mac_param: bool,
508 /// The direction the next box made by `hpack` or `vpackage` gets
509 /// (from `dir`/`bdir` of a box specification), or -1. It is set before
510 /// packing because over- and underfull box reports show it.
511 pub pack_dir: i8,
512 /// `\randomseed`: the seed `\setrandomseed` last gave (its absolute
513 /// value), or the one chosen at startup.
514 pub random_seed: i32,
515 /// The state of the random number generator (METAFONT's, see
516 /// [`retex_arith::random`]) behind `\uniformdeviate`, `\normaldeviate`
517 /// and the `tex` library's randomizers.
518 pub randoms: retex_arith::random::Randoms,
519 /// `\lastxpos`.
520 pub last_x_pos: i32,
521 /// `\lastypos`.
522 pub last_y_pos: i32,
523 /// The magnification frozen by `prepare_mag`.
524 pub mag_set: i32,
525 /// The font dimension located by `find_font_dimen`.
526 pub font_dimen_loc: Option<(u32, u32)>,
527 /// `\showboxdepth` in effect while displaying boxes.
528 pub depth_threshold: i32,
529 /// `\showboxbreadth` in effect while displaying boxes.
530 pub breadth_max: i32,
531 /// The paragraph-ending control sequence (`\partokenname`).
532 pub par_token_cs: CsId,
533 /// Whether `\dump` ended the run.
534 pub dump_requested: bool,
535 /// Whether a macro expanded now would have its body executed by main
536 /// control (see [`native`]).
537 pub exec_context: bool,
538 /// Whether the next [`expand`](Engine::expand) is a single expansion
539 /// step whose result is not expanded further (`\expandafter`, `\span`
540 /// in a preamble, Lua's `token.expand`): expandable native
541 /// implementations do not run there (see [`native`]). `expand` clears
542 /// it on entry.
543 pub one_step: bool,
544 /// Where macros are defined (`retex profile`, `retex explain`).
545 pub origins: Option<Box<profile::Origins>>,
546 /// The sampling profiler (`retex profile`).
547 pub profiler: Option<Box<profile::Profiler>>,
548 /// The figure cache's recording (`docs/design/figure-cache.md`).
549 pub figures: figures::Figures,
550 /// Integer parameters set from the command line when the job starts
551 /// ([`init::Options::int_params`]).
552 pub start_ints: Vec<(u32, i32)>,
553 /// `-output-comment`.
554 pub output_comment: Option<String>,
555 /// The job's start time (`\time`, the banner, the PDF creation date).
556 pub start_time: clock::StartTime,
557 /// The page builder.
558 pub page: typeset::PageState,
559 /// Lua functions registered for `\luafunction` (by number).
560 pub lua_functions: std::collections::HashMap<i32, u32>,
561 /// Called to store the format when `\dump` is executed.
562 pub format_hook: Option<EngineHook>,
563 /// Called at the end of the run to finish output files.
564 pub finish_hook: Option<EngineHook>,
565 /// Non-immediate `\write`s waiting for shipout.
566 pub deferred_writes: Vec<(i32, token::TokenList)>,
567 /// Text directions opened in the current box: (group level,
568 /// direction, the text direction before), innermost last (see
569 /// [`dirs`]).
570 pub text_dirs: Vec<(u16, i8, i8)>,
571 /// Box resources to write after the page or form being written, one
572 /// list per open page or form.
573 pub pending_forms: Vec<Vec<u32>>,
574 /// The packets of virtual characters whose `lua` command is running,
575 /// innermost last: what the `vf` library draws into.
576 pub(crate) vf_frames: Vec<shipout::VfFrame>,
577 /// Objects referred to (`\pdfextension refobj`) in the box resources
578 /// being written, one list per open form.
579 pub form_objects: Vec<Vec<pdfext::PdfObject>>,
580 /// The `text_dirs` of the enclosing boxes.
581 pub outer_text_dirs: Vec<Vec<(u16, i8, i8)>>,
582 /// The math and text directions outside the open formulas, with the
583 /// save level of each formula's group.
584 pub outer_math_dirs: Vec<(u16, i32, i32)>,
585 /// Contents of files written by `\write`, for the pass driver.
586 pub written_sink: files::WrittenSink,
587 /// State of the typesetting routines.
588 pub ts: typeset::TsState,
589 /// Tokens printed from Lua waiting to be pushed as a token list.
590 pub pending_lua_tokens: Option<Vec<Token>>,
591 /// Default output mode: errors and warnings recorded for the driver
592 /// (`None` in compatibility mode).
593 pub diagnostics: Option<Vec<diag::Diagnostic>>,
594 /// Default output mode, interactive: shows the diagnostics recorded
595 /// since the last prompt, and the prompt, before the terminal is read.
596 /// Returns `false` if it showed nothing (the error only repeats one
597 /// already reported): TeX then goes on without asking.
598 pub prompt_hook: Option<PromptHook>,
599 /// Default output mode: called when a page is shipped or a file opened,
600 /// to show progress and the errors recorded so far.
601 pub progress_hook: Option<fn(&mut Engine)>,
602 /// The number of diagnostics the prompt hook has shown.
603 pub diagnostics_shown: usize,
604 /// Whether the terminal was read (an error or a `\read` prompt).
605 pub prompted: bool,
606 /// Image resources (`\saveimageresource`), in the order saved (their
607 /// indices are their object numbers).
608 pub images: Vec<image::ImageResource>,
609 /// `\lastsavedimageresourceindex`.
610 pub last_image_index: i32,
611 /// `\lastsavedimageresourcepages`.
612 pub last_image_pages: i32,
613 /// Whether SVG text has been given its font finder.
614 pub svg_fonts_set: bool,
615 /// Box resources (`\saveboxresource`), in the order saved (their
616 /// indices are their object numbers).
617 pub box_resources: Vec<image::BoxResource>,
618 /// `\lastsavedboxresourceindex`.
619 pub last_box_index: i32,
620 /// Scratch buffer for macro arguments (reused by `macro_call`).
621 pub arg_scratch: Vec<Token>,
622 /// Scratch buffer for the argument list of a macro call.
623 pub param_scratch: Vec<token::TokenList>,
624 /// The file of each `\write` stream opened (see [`snapshot`]).
625 pub write_paths: Vec<Option<std::path::PathBuf>>,
626 /// The macro at whose expansion [`snapshot_hook`](Self::snapshot_hook)
627 /// runs (LaTeX's `\document`).
628 pub snapshot_cs: Option<CsId>,
629 /// Called once, when `snapshot_cs` is expanded (see [`snapshot`]).
630 pub snapshot_hook: Option<snapshot::Hook>,
631 /// Called once, at the top of the outermost main loop (between two
632 /// commands, nothing of the engine's own on the call stack): where the
633 /// browser takes its preamble snapshot, a memory image that
634 /// [`Engine::resume_run`] continues from.
635 pub loop_hook: Option<fn(&mut Engine)>,
636 /// Called after each page is shipped out, to take a page checkpoint,
637 /// and once when the document has begun (see [`snapshot`]).
638 pub page_hook: Option<fn(&mut Engine)>,
639 /// Called when the run is over, before the output file is finished
640 /// (see [`snapshot`]).
641 pub final_hook: Option<fn(&mut Engine)>,
642 /// The input level of the file `\begin{document}` was read from: the
643 /// page hook runs before its next line is read.
644 pub(crate) begun_level: Option<usize>,
645 /// Hashes of the code-table pages and macros that existed when
646 /// fingerprints were prepared (see [`fingerprint`]).
647 pub fp_cache: Option<Box<fingerprint::FpCache>>,
648 /// The file and line the user asked to edit at an error (`E`); the
649 /// program runs the editor at the end.
650 pub edit_request: Option<(Vec<u8>, i32)>,
651 /// The output file written at the end: name, pages and bytes.
652 pub output_written: Option<(String, u32, u64)>,
653 /// The line number reported while reading Lua output.
654 pub input_line_for_lua: i32,
655 /// Internal tokens made by `\pdfvariable`, by variable name.
656 pub backend_cs: std::collections::HashMap<&'static str, CsId>,
657 /// Nesting depth of local control loops (`tex.runtoks`).
658 pub local_level: u32,
659 /// The box `token.scan_list` scanned, once finished.
660 pub lua_scanned_box: Option<nodes::NodeRef>,
661 /// Whether `token.scan_list` runs local control for its box.
662 pub lua_scan_waiting: bool,
663 /// The output backend (PDF writer), if any.
664 pub backend: Option<Box<dyn shipout::Backend>>,
665 /// The DVI writer (`\outputmode=0`).
666 pub dvi: dvi::DviWriter,
667 /// Hyphenation languages, by `\language` number.
668 pub languages: Vec<Option<retex_hyph::Language>>,
669 /// Document-level PDF data from `\pdfextension`.
670 pub pdf: pdfext::PdfState,
671 /// Current values of the colour stacks during shipout.
672 pub colorstack_values: std::collections::HashMap<i32, Vec<Vec<u8>>>,
673 /// Number of pages shipped out.
674 pub total_pages: u32,
675 /// Muglue values of `\Umath…` spacing parameters.
676 pub math_glues: Vec<glue::Glue>,
677 /// Whether the parameters set by the first formula are initialized
678 /// (see [`mathparams`]).
679 pub math_params_initialized: bool,
680 /// SyncTeX output.
681 pub synctex: synctex::Synctex,
682}
683
684impl std::fmt::Debug for Engine {
685 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
686 f.debug_struct("Engine")
687 .field("cur_cmd", &self.cur_cmd)
688 .field("cur_chr", &self.cur_chr)
689 .finish_non_exhaustive()
690 }
691}