Skip to main content

retex_engine/
backend.rs

1//! Backend variables and feedback (`\pdfvariable`, `\pdffeedback`,
2//! `\dvivariable`, `\dvifeedback`).
3//!
4//! `\pdfvariable name` expands to an anonymous token standing for an
5//! internal parameter; assignments and `\the` then work on it like on any
6//! parameter. The parameter indices are those LuaTeX reports through
7//! `\meaning` (e.g. `[unknown command code! (88, 137)]` for
8//! `compresslevel`).
9
10use std::rc::Rc;
11
12use crate::Engine;
13use crate::cmd::Cmd;
14use crate::eqtb::Meaning;
15use crate::token::Token;
16
17/// Kinds of backend variables.
18#[derive(Clone, Copy, Debug, PartialEq, Eq)]
19pub enum VarKind {
20    /// An integer parameter.
21    Int,
22    /// A dimension parameter.
23    Dimen,
24    /// A token-list parameter.
25    Toks,
26}
27
28/// `\pdfvariable horigin` (index of the dimension parameter).
29pub const H_ORIGIN: u32 = 28;
30/// `\pdfvariable vorigin` (index of the dimension parameter).
31pub const V_ORIGIN: u32 = 29;
32/// `\pdfvariable compresslevel`.
33pub const COMPRESS_LEVEL: u32 = 137;
34/// `\pdfvariable decimaldigits`.
35pub const DECIMAL_DIGITS: u32 = 138;
36/// `\pdfvariable objcompresslevel`.
37pub const OBJ_COMPRESS_LEVEL: u32 = 152;
38/// `\pdfvariable minorversion`.
39pub const MINOR_VERSION: u32 = 143;
40
41/// `\pdfvariable majorversion` (index of the integer parameter).
42pub const MAJOR_VERSION: u32 = 142;
43/// `\pdfvariable pageattr` (index of the token-list parameter).
44pub const PAGE_ATTR: u32 = 11;
45/// `\pdfvariable pageresources`.
46pub const PAGE_RESOURCES: u32 = 12;
47/// `\pdfvariable pagesattr`.
48pub const PAGES_ATTR: u32 = 13;
49/// `\pdfvariable trailerid`.
50pub const TRAILER_ID: u32 = 17;
51
52/// The PDF backend variables: name, kind and parameter index.
53pub const PDF_VARIABLES: &[(&str, VarKind, u32)] = &[
54    ("compresslevel", VarKind::Int, 137),
55    ("decimaldigits", VarKind::Int, 138),
56    ("imageresolution", VarKind::Int, 139),
57    ("pkresolution", VarKind::Int, 140),
58    ("uniqueresname", VarKind::Int, 141),
59    ("majorversion", VarKind::Int, 142),
60    ("minorversion", VarKind::Int, 143),
61    ("pagebox", VarKind::Int, 144),
62    ("inclusionerrorlevel", VarKind::Int, 145),
63    ("ignoreunknownimages", VarKind::Int, 146),
64    ("gamma", VarKind::Int, 147),
65    ("imageapplygamma", VarKind::Int, 148),
66    ("imagegamma", VarKind::Int, 149),
67    ("imagehicolor", VarKind::Int, 150),
68    ("imageaddfilename", VarKind::Int, 151),
69    ("objcompresslevel", VarKind::Int, 152),
70    ("inclusioncopyfonts", VarKind::Int, 153),
71    ("gentounicode", VarKind::Int, 154),
72    ("pkfixeddpi", VarKind::Int, 155),
73    ("suppressoptionalinfo", VarKind::Int, 156),
74    ("omitcidset", VarKind::Int, 157),
75    ("recompress", VarKind::Int, 158),
76    ("omitcharset", VarKind::Int, 159),
77    ("omitinfodict", VarKind::Int, 160),
78    ("omitmediabox", VarKind::Int, 161),
79    ("omitprocset", VarKind::Int, 163),
80    ("ptexprefix", VarKind::Int, 164),
81    ("horigin", VarKind::Dimen, 28),
82    ("vorigin", VarKind::Dimen, 29),
83    ("threadmargin", VarKind::Dimen, 30),
84    ("destmargin", VarKind::Dimen, 31),
85    ("linkmargin", VarKind::Dimen, 32),
86    ("xformmargin", VarKind::Dimen, 33),
87    ("pageattr", VarKind::Toks, 11),
88    ("pageresources", VarKind::Toks, 12),
89    ("pagesattr", VarKind::Toks, 13),
90    ("xformattr", VarKind::Toks, 14),
91    ("xformresources", VarKind::Toks, 15),
92    ("pkmode", VarKind::Toks, 16),
93    ("trailerid", VarKind::Toks, 17),
94];
95
96impl Engine {
97    /// Expands `\pdfvariable name` to its internal token.
98    pub fn expand_backend_variable(&mut self) {
99        let found = PDF_VARIABLES
100            .iter()
101            .find(|(name, _, _)| self.scan_keyword(name))
102            .copied();
103        let Some((name, kind, index)) = found else {
104            return;
105        };
106        let cs = match self.backend_cs.get(name) {
107            Some(&cs) => cs,
108            None => {
109                let (label, cmd) = match kind {
110                    VarKind::Int => ("[internal backend integer]", Cmd::AssignInt),
111                    VarKind::Dimen => ("[internal backend dimension]", Cmd::AssignDimen),
112                    VarKind::Toks => ("[internal backend tokenlist]", Cmd::AssignToks),
113                };
114                let cs = self.hash.new_internal(label.as_bytes());
115                self.eqtb.ensure_cs(cs);
116                self.eqtb.cs[cs.0 as usize].val = Meaning::prim(cmd, index);
117                self.eqtb.cs[cs.0 as usize].level = crate::eqtb::LEVEL_ONE;
118                self.backend_cs.insert(name, cs);
119                cs
120            }
121        };
122        self.cur_tok = Token::cs(cs);
123        self.back_input();
124    }
125
126    /// Expands `\pdffeedback name`.
127    pub fn expand_backend_feedback(&mut self) {
128        if self.eqtb.int(crate::primitives_gen::assign_int::OUTPUTMODE) <= 0 {
129            // Observed: an error in DVI mode; the keyword is not read.
130            self.print_err("unexpected use of \\pdffeedback");
131            self.help(&[]);
132            self.error();
133            return;
134        }
135        const KEYS: &[&str] = &[
136            "version",
137            "revision",
138            "lastlink",
139            "retval",
140            "lastobj",
141            "lastannot",
142            "xformname",
143            "creationdate",
144            "fontname",
145            "fontobjnum",
146            "fontsize",
147            "pageref",
148            "colorstackinit",
149        ];
150        let key = KEYS.iter().find(|k| self.scan_keyword(k)).copied();
151        let text: Vec<u8> = match key {
152            Some("version") => b"140".to_vec(),
153            Some("lastobj") => self.pdf.last_obj.to_string().into_bytes(),
154            Some("lastannot") => self.pdf.last_annot.to_string().into_bytes(),
155            Some("lastlink") => self.pdf.last_link.to_string().into_bytes(),
156            Some("revision") => b"0".to_vec(),
157            Some("retval") => self.pdf.retval.to_string().into_bytes(),
158            Some("creationdate") => self.pdf_creation_date().into_bytes(),
159            Some(k @ ("fontname" | "fontobjnum" | "fontsize")) => {
160                self.scan_font_ident();
161                let f = self.cur_val.max(0) as u32;
162                if f == 0 {
163                    // A fatal error in LuaTeX.
164                    self.print_err(&format!("invalid font identifier when asking '{k}'"));
165                    self.help(&[]);
166                    self.error();
167                    return self.ins_list(Rc::from(crate::the::str_toks(b"0")));
168                }
169                match k {
170                    "fontname" => self.pdf_font(f).0.to_string().into_bytes(),
171                    "fontobjnum" => self.pdf_font(f).1.to_string().into_bytes(),
172                    _ => {
173                        let mut out = String::new();
174                        retex_arith::print_scaled(&mut out, self.font(f).size);
175                        out.push_str("pt");
176                        out.into_bytes()
177                    }
178                }
179            }
180            Some("pageref") => {
181                self.scan_int();
182                let page = self.cur_val;
183                if page <= 0 {
184                    self.print_err("invalid page number when asking 'pageref'");
185                    self.help(&[]);
186                    self.error();
187                    b"0".to_vec()
188                } else {
189                    self.pdf_page_ref(page).to_string().into_bytes()
190                }
191            }
192            Some("colorstackinit") => {
193                let page = self.scan_keyword("page");
194                let direct = self.scan_keyword("direct");
195                let toks = self.scan_toks_expanded();
196                let init =
197                    self.print_to_string(|e| e.show_token_list(&toks, 0, toks.len(), i64::MAX));
198                self.pdf_new_colorstack(
199                    init,
200                    page,
201                    if direct {
202                        crate::pdfext::literal_mode::DIRECT
203                    } else {
204                        crate::pdfext::literal_mode::ORIGIN
205                    },
206                )
207                .to_string()
208                .into_bytes()
209            }
210            Some(_) => b"0".to_vec(),
211            None => Vec::new(),
212        };
213        let toks = crate::the::str_toks(&text);
214        self.ins_list(Rc::from(toks));
215    }
216
217    /// The creation date in PDF format, as `\pdffeedback creationdate`.
218    pub fn pdf_creation_date(&self) -> String {
219        self.start_time.pdf_date()
220    }
221}