Skip to main content

rustex_lib/engine/
mod.rs

1use crate::engine::extension::CSS;
2use crate::shipout;
3use crate::shipout::html::{CompilationDisplay, ImageOptions};
4use crate::shipout::state::{FontData, Shipout, ShipoutNodeV, ShipoutWrapper, Top};
5use crate::utils::{VecMap, VecSet};
6use extension::RusTeXExtension;
7use fonts::Fontsystem;
8use nodes::RusTeXNode;
9use output::RusTeXOutput;
10use state::RusTeXState;
11use std::fmt::Display;
12use std::io::BufWriter;
13use std::path::{Path, PathBuf};
14use std::sync::Mutex;
15use stomach::RusTeXStomach;
16use tex_engine::commands::{Macro, TeXCommand};
17use tex_engine::engine::filesystem::FileSystem;
18use tex_engine::engine::filesystem::{File, SourceReference, VirtualFile};
19use tex_engine::engine::fontsystem::FontSystem;
20use tex_engine::engine::gullet::DefaultGullet;
21use tex_engine::engine::gullet::Gullet;
22use tex_engine::engine::mouth::DefaultMouth;
23use tex_engine::engine::mouth::Mouth;
24use tex_engine::engine::state::State as OrigState;
25use tex_engine::engine::stomach::Stomach as StomachT;
26use tex_engine::engine::utils::memory::MemoryManager;
27use tex_engine::engine::EngineExtension;
28use tex_engine::engine::TeXEngine;
29use tex_engine::engine::{DefaultEngine, EngineAux, EngineReferences, EngineTypes};
30use tex_engine::pdflatex::nodes::PDFColor;
31use tex_engine::pdflatex::PDFTeXEngine;
32use tex_engine::prelude::*;
33use tex_engine::tex;
34use tex_engine::tex::catcodes::AT_LETTER_SCHEME;
35use tex_engine::tex::numerics::{Dim32, Mu};
36use tex_engine::tex::tokens::CompactToken;
37use tex_engine::utils::errors::{ErrorThrower, TeXError, TeXResult};
38use tex_engine::utils::HMap;
39
40pub mod commands;
41pub(crate) mod extension;
42pub mod files;
43pub mod fonts;
44pub(crate) mod nodes;
45pub mod output;
46pub(crate) mod pgf;
47pub mod state;
48pub mod stomach;
49
50pub type Extension = RusTeXExtension;
51pub(crate) type Font = tex_engine::engine::fontsystem::TfmFont<i32, Dim32, InternedCSName<u8>>;
52pub(crate) type SRef = SourceReference<<<Types as EngineTypes>::File as File>::SourceRefID>;
53pub(crate) type Refs<'a, 'b> = &'a mut EngineReferences<'b, Types>;
54pub(crate) type CSName = InternedCSName<u8>;
55
56#[derive(Clone, Debug, Copy)]
57pub struct Types;
58
59pub type Res<R> = TeXResult<R, Types>;
60
61impl EngineTypes for Types {
62    type Char = u8;
63    type CSName = CSName;
64    type Token = CompactToken;
65    type Extension = Extension;
66    type File = VirtualFile<u8>;
67    type FileSystem = files::RusTeXFileSystem;
68    type Int = i32;
69    type Dim = Dim32;
70    type MuDim = Mu;
71    type Num = tex::numerics::DefaultNumSet;
72    type State = RusTeXState;
73    type Outputs = RusTeXOutput;
74    type Mouth = DefaultMouth<Self>;
75    type Gullet = DefaultGullet<Self>;
76    type Stomach = RusTeXStomach;
77    type CustomNode = RusTeXNode;
78    type Font = tex_engine::engine::fontsystem::TfmFont<i32, Dim32, InternedCSName<u8>>;
79    type FontSystem = Fontsystem;
80    type ErrorHandler = ErrorThrower<Self>;
81}
82
83thread_local! {
84    static MAIN_STATE : Mutex<Option<(RusTeXState,MemoryManager<CompactToken>)>> = const { Mutex::new(None) };
85    static FONT_SYSTEM : Mutex<Option<Fontsystem>> = const { Mutex::new(None) };
86}
87
88fn get_state(log: bool) -> (RusTeXState, MemoryManager<CompactToken>) {
89    MAIN_STATE.with(|state| {
90        let mut guard = state.lock().unwrap();
91        match &mut *guard {
92            Some((s, m)) => (s.clone(), m.clone()),
93            n => {
94                //let start = std::time::Instant::now();
95                let mut engine = DefaultEngine::<Types>::default();
96                if log {
97                    engine.aux.outputs = RusTeXOutput::Print(true);
98                }
99                commands::register_primitives_preinit(&mut engine);
100                engine.initialize_pdflatex().unwrap();
101                commands::register_primitives_postinit(&mut engine);
102                engine.init_file("rustex_defs.def").unwrap();
103                *n = Some((engine.state.clone(), engine.aux.memory.clone()));
104                FONT_SYSTEM.with(|f| f.lock().unwrap().replace(engine.fontsystem.clone()));
105                //println!("Initialized in {:?}", start.elapsed());
106                (engine.state, engine.aux.memory)
107            }
108        }
109    })
110}
111
112fn get_engine(log: bool) -> DefaultEngine<Types> {
113    let (mut state, mut memory) = get_state(log);
114    let fontsystem = FONT_SYSTEM.with(|f| f.lock().unwrap().clone()).unwrap();
115    let mut aux = EngineAux {
116        outputs: RusTeXOutput::None,
117        error_handler: ErrorThrower::new(),
118        start_time: chrono::Local::now(),
119        extension: Extension::new(&mut memory),
120        memory,
121        jobname: String::new(),
122    };
123    let mut mouth = DefaultMouth::new(&mut aux, &mut state);
124    let gullet = DefaultGullet::new(&mut aux, &mut state, &mut mouth);
125    let stomach = RusTeXStomach::new(&mut aux, &mut state);
126    DefaultEngine {
127        state,
128        aux,
129        fontsystem,
130        filesystem: files::RusTeXFileSystem::new(tex_engine::utils::PWD.to_path_buf()),
131        mouth,
132        gullet,
133        stomach,
134    }
135}
136
137pub struct CompilationResult {
138    out: Vec<ShipoutNodeV>,
139    pub error: Option<(TeXError<Types>, Vec<FileTrace>)>,
140    pub font_data: HMap<Box<str>, FontData>,
141    top_font: Font,
142    top_width: i32,
143    page_width: i32,
144    sourcerefs: bool,
145    metas: Vec<VecMap<String, String>>,
146    top: VecMap<String, String>,
147    img: ImageOptions,
148    css: VecSet<CSS>,
149    pub font_info: bool,
150}
151impl CompilationResult {
152    pub fn write_out(&self, path: &Path) -> std::io::Result<()> {
153        use std::io::Write;
154        let mut f = std::fs::File::create(path)?;
155        let mut f = BufWriter::new(&mut f);
156        write!(f, "{self}")?;
157        f.flush()
158    }
159}
160impl Display for CompilationResult {
161    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
162        let mut dsp = CompilationDisplay {
163            color: PDFColor::default(),
164            font: self.top_font.clone(),
165            width: self.top_width,
166            indent: 0,
167            in_link: false,
168            attrs: VecMap::default(),
169            styles: VecMap::default(),
170            sourcerefs: self.sourcerefs,
171            font_data: &self.font_data,
172            image: &self.img,
173            font_info: self.font_info,
174            f,
175        };
176        dsp.display(
177            &self.metas,
178            &self.top,
179            &self.css.inner,
180            self.page_width,
181            &self.out,
182        )
183    }
184}
185
186pub trait RusTeXEngineT {
187    fn initialize(log: bool);
188    fn get() -> Self;
189    fn run<S: AsRef<str>>(&mut self, file: S, settings: Settings) -> CompilationResult;
190    fn do_file<S: AsRef<str>>(file: S, settings: Settings) -> CompilationResult;
191}
192
193pub(crate) fn register_command(
194    e: &mut DefaultEngine<Types>,
195    globally: bool,
196    name: &'static str,
197    sig: &'static str,
198    exp: &'static str,
199    protect: bool,
200    long: bool,
201) {
202    let e = e.get_engine_refs();
203    let name = e.aux.memory.cs_interner_mut().cs_from_str(name);
204    let mut cmd =
205        Macro::new::<_, _, Types>(e.aux.memory.cs_interner_mut(), &AT_LETTER_SCHEME, sig, exp)
206            .unwrap();
207    if protect {
208        cmd.protected = true
209    }
210    if long {
211        cmd.long = true
212    }
213    e.state
214        .set_command(e.aux, name, Some(TeXCommand::Macro(cmd)), globally)
215}
216
217#[derive(Default)]
218pub struct Settings {
219    pub sourcerefs: bool,
220    pub verbose: bool,
221    pub log: bool,
222    pub image_options: ImageOptions,
223    pub insert_font_info: bool,
224}
225
226/*pub struct RusTeXEngine {
227    engine: DefaultEngine<Types>
228}*/
229pub type RusTeXEngine = DefaultEngine<Types>;
230
231mod sealed {
232    pub trait Sealed {}
233}
234impl sealed::Sealed for RusTeXEngine {}
235
236pub trait RusTeXEngineExt: sealed::Sealed {
237    fn run_string(&mut self, file: PathBuf, content: &str) -> Option<TeXError<Types>>;
238    fn do_result(
239        &mut self,
240        result: Option<TeXError<Types>>,
241        settings: Settings,
242    ) -> CompilationResult;
243}
244
245impl RusTeXEngineExt for RusTeXEngine {
246    fn run_string(&mut self, file: PathBuf, content: &str) -> Option<TeXError<Types>> {
247        let s = file.display().to_string();
248        self.filesystem
249            .set_pwd(file.parent().unwrap().to_path_buf());
250        self.filesystem.add_file(file, content);
251        let r = match self.do_file_pdf(&s, shipout::shipout) {
252            Ok(_) => None,
253            Err(e) => {
254                self.aux.outputs.errmessage(format!(
255                    "{}\n\nat {}",
256                    e,
257                    self.mouth.current_sourceref().display(&self.filesystem)
258                ));
259                Some(e)
260            }
261        };
262        {
263            let mut s = std::mem::take(&mut self.aux.extension.state);
264            let mut refs = self.get_engine_refs();
265            let mut istate = Shipout {
266                nodes: std::mem::take(&mut s.output),
267                state: Top,
268                wrapper: std::mem::take(&mut s.wrapper),
269                previous: std::mem::take(&mut s.previous),
270                top_state: &mut s,
271                engine: &mut refs,
272            };
273            let _ = ShipoutWrapper::close_all(&mut istate);
274            let nodes = std::mem::take(&mut istate.nodes);
275            drop(istate);
276            drop(refs);
277            s.output = nodes;
278            self.aux.extension.state = s;
279        }
280        r
281    }
282    fn do_result(
283        &mut self,
284        result: Option<TeXError<Types>>,
285        settings: Settings,
286    ) -> CompilationResult {
287        let result = result.map(|e| {
288            let filetrace = self
289                .mouth
290                .file_trace()
291                .filter_map(|sr| {
292                    let id = sr.file?;
293                    let file = self.filesystem.ref_str(Some(id));
294                    let file = self
295                        .filesystem
296                        .inner
297                        .kpse
298                        .pwd
299                        .join(file)
300                        .canonicalize()
301                        .ok()?;
302                    Some(FileTrace {
303                        file,
304                        line: sr.line as u32,
305                        col: sr.column as u32,
306                    })
307                })
308                .collect::<Vec<_>>();
309            (e, filetrace)
310        });
311        {
312            let mut s = std::mem::take(&mut self.aux.extension.state);
313            let mut refs = self.get_engine_refs();
314            let mut istate = Shipout {
315                nodes: std::mem::take(&mut s.output),
316                state: Top,
317                wrapper: std::mem::take(&mut s.wrapper),
318                previous: std::mem::take(&mut s.previous),
319                top_state: &mut s,
320                engine: &mut refs,
321            };
322            let _ = ShipoutWrapper::close_all(&mut istate);
323            let nodes = std::mem::take(&mut istate.nodes);
324            drop(istate);
325            drop(refs);
326            s.output = nodes;
327            self.aux.extension.state = s;
328        }
329        let out = std::mem::take(&mut self.aux.extension.state.output);
330        let css = std::mem::take(&mut self.aux.extension.css);
331        let font_data = std::mem::take(&mut self.aux.extension.state.font_data);
332        let top_font = self
333            .aux
334            .extension
335            .state
336            .top_font
337            .clone()
338            .unwrap_or_else(|| self.fontsystem.null());
339        let top_width = self
340            .aux
341            .extension
342            .state
343            .top_width
344            .clone()
345            .unwrap_or_default();
346        let page_width = self
347            .aux
348            .extension
349            .state
350            .page_width
351            .clone()
352            .unwrap_or_default();
353        let top = std::mem::take(&mut self.aux.extension.top);
354        let metas = std::mem::take(&mut self.aux.extension.metas);
355        CompilationResult {
356            out,
357            error: result,
358            css,
359            font_data,
360            top_font,
361            top_width,
362            top,
363            metas,
364            page_width,
365            sourcerefs: settings.sourcerefs,
366            font_info: settings.insert_font_info,
367            img: settings.image_options,
368        }
369    }
370}
371
372#[derive(Debug, Clone)]
373pub struct FileTrace {
374    pub file: PathBuf,
375    pub line: u32,
376    pub col: u32,
377}
378
379impl RusTeXEngineT for RusTeXEngine {
380    fn initialize(log: bool) {
381        let _ = get_engine(log);
382    }
383
384    fn get() -> Self {
385        get_engine(false)
386    }
387
388    fn run<S: AsRef<str>>(&mut self, file: S, settings: Settings) -> CompilationResult {
389        let res = match self.do_file_pdf(file.as_ref(), shipout::shipout) {
390            Ok(_) => None,
391            Err(e) => {
392                self.aux.outputs.errmessage(format!(
393                    "{}\n\nat {}",
394                    e,
395                    self.mouth.current_sourceref().display(&self.filesystem)
396                ));
397                Some(e)
398            }
399        };
400        self.do_result(res, settings)
401    }
402
403    fn do_file<S: AsRef<str>>(file: S, settings: Settings) -> CompilationResult {
404        let mut engine = Self::get();
405        engine.stomach.continuous = true;
406        if settings.log {
407            engine.aux.outputs = RusTeXOutput::Print(settings.verbose);
408        }
409        let ret = <Self as RusTeXEngineT>::run(&mut engine, file, settings);
410        FONT_SYSTEM.with(|f| f.lock().unwrap().replace(engine.fontsystem));
411        ret
412    }
413}