1use md5::Digest;
2
3use super::nodes::{
4 ColorStackAction, NumOrName, PDFAnnot, PDFBoxSpec, PDFCatalog, PDFColor, PDFDest, PDFExtension,
5 PDFImage, PDFLiteral, PDFLiteralOption, PDFNode, PDFObj, PDFOutline, PDFStartLink, PDFXForm,
6 PDFXImage,
7};
8use crate::commands::primitives::*;
9use crate::commands::CommandScope;
10use crate::engine::filesystem::{File, FileSystem};
11use crate::engine::fontsystem::Font;
12use crate::engine::gullet::Gullet;
13use crate::engine::state::State;
14use crate::engine::stomach::Stomach;
15use crate::engine::stomach::TeXMode;
16use crate::engine::{EngineReferences, EngineTypes, TeXEngine};
17use crate::pdflatex::{FileWithMD5, FontWithLpRp};
18use crate::prelude::CSHandler;
19use crate::prelude::Character;
20use crate::tex::catcodes::CommandCode;
21use crate::tex::nodes::horizontal::HNode;
22use crate::tex::nodes::math::MathNode;
23use crate::tex::nodes::vertical::VNode;
24use crate::tex::nodes::WhatsitFunction;
25use crate::tex::numerics::NumSet;
26use crate::tex::tokens::token_lists::Otherize;
27use crate::tex::tokens::{StandardToken, Token};
28use crate::utils::errors::{TeXError, TeXResult};
29use std::fmt::Write;
30
31pub fn pdftexversion<ET: EngineTypes>(
32 _engine: &mut EngineReferences<ET>,
33 _tk: ET::Token,
34) -> TeXResult<ET::Int, ET> {
35 Ok(<ET::Num as NumSet>::Int::from(140))
36}
37
38pub fn pdfmajorversion<ET: EngineTypes>(
39 _engine: &mut EngineReferences<ET>,
40 _tk: ET::Token,
41) -> TeXResult<ET::Int, ET> {
42 Ok(<ET::Num as NumSet>::Int::from(1))
43}
44
45pub fn pdftexrevision<ET: EngineTypes>(
46 _engine: &mut EngineReferences<ET>,
47 exp: &mut Vec<ET::Token>,
48 _tk: ET::Token,
49) -> TeXResult<(), ET> {
50 exp.push(ET::Token::from_char_cat(b'2'.into(), CommandCode::Other));
51 exp.push(ET::Token::from_char_cat(b'5'.into(), CommandCode::Other));
52 Ok(())
53}
54
55pub fn pdfcatalog<ET: EngineTypes>(
56 engine: &mut EngineReferences<ET>,
57 tk: ET::Token,
58) -> TeXResult<(), ET>
59where
60 ET::Extension: PDFExtension<ET>,
61 ET::CustomNode: From<PDFNode<ET>>,
62{
63 let mut literal = String::new();
64 engine.read_braced_string(true, true, &tk, &mut literal)?;
65 let action = if engine.read_keyword(b"openaction")? {
66 Some(super::nodes::action_spec(engine, &tk)?)
67 } else {
68 None
69 };
70 let node = PDFNode::PDFCatalog(PDFCatalog { literal, action });
71 crate::add_node!(ET::Stomach;engine,VNode::Custom(node.into()),HNode::Custom(node.into()),MathNode::Custom(node.into()));
72 Ok(())
73}
74
75pub fn pdfcolorstack<ET: EngineTypes>(
76 engine: &mut EngineReferences<ET>,
77 tk: ET::Token,
78) -> TeXResult<(), ET>
79where
80 ET::Extension: PDFExtension<ET>,
81 ET::CustomNode: From<PDFNode<ET>>,
82{
83 let index = engine.read_int(false, &tk)?.into();
84 if index < 0 || index >= (engine.aux.extension.colorstacks().len() as i64) {
85 engine.general_error(format!("Unknown color stack number {}", index))?;
86 }
87 let index = index as usize;
88 let kw = engine.read_keywords(&[b"push", b"pop", b"set", b"current"])?;
89 match kw {
90 Some(b"current") => crate::add_node!(ET::Stomach;engine,
91 VNode::Custom(PDFNode::Color(ColorStackAction::Current(index)).into()),
92 HNode::Custom(PDFNode::Color(ColorStackAction::Current(index)).into()),
93 MathNode::Custom(PDFNode::Color(ColorStackAction::Current(index)).into())
94 ),
95 Some(b"pop") => {
96 crate::add_node!(ET::Stomach;engine,
97 VNode::Custom(PDFNode::Color(ColorStackAction::Pop(index)).into()),
98 HNode::Custom(PDFNode::Color(ColorStackAction::Pop(index)).into()),
99 MathNode::Custom(PDFNode::Color(ColorStackAction::Pop(index)).into())
100 )
101 }
102 Some(b"set") => {
103 let mut color = String::new();
104 engine.read_braced_string(true, true, &tk, &mut color)?;
105 let color = PDFColor::parse(color);
106 crate::add_node!(ET::Stomach;engine,
107 VNode::Custom(PDFNode::Color(ColorStackAction::Set(index,color)).into()),
108 HNode::Custom(PDFNode::Color(ColorStackAction::Set(index,color)).into()),
109 MathNode::Custom(PDFNode::Color(ColorStackAction::Set(index,color)).into())
110 )
111 }
112 Some(b"push") => {
113 let mut color = String::new();
114 engine.read_braced_string(true, true, &tk, &mut color)?;
115 let color = PDFColor::parse(color);
116 crate::add_node!(ET::Stomach;engine,
117 VNode::Custom(PDFNode::Color(ColorStackAction::Push(index,color)).into()),
118 HNode::Custom(PDFNode::Color(ColorStackAction::Push(index,color)).into()),
119 MathNode::Custom(PDFNode::Color(ColorStackAction::Push(index,color)).into())
120 )
121 }
122 _ => TeXError::missing_keyword(
123 engine.aux,
124 engine.state,
125 engine.mouth,
126 &["current", "pop", "set", "push"],
127 )?,
128 }
129 Ok(())
130}
131
132pub fn pdfcolorstackinit<ET: EngineTypes>(
133 engine: &mut EngineReferences<ET>,
134 tk: ET::Token,
135) -> TeXResult<ET::Int, ET>
136where
137 ET::Extension: PDFExtension<ET>,
138{
139 engine.read_keyword(b"page")?;
140 engine.read_keyword(b"direct")?;
141 let mut color = String::new();
142 engine.read_braced_string(false, true, &tk, &mut color)?;
143 let color = PDFColor::parse(color);
144 let idx = engine.aux.extension.colorstacks().len() as i32;
145 engine.aux.extension.colorstacks().push(vec![color]);
146 Ok(ET::Int::from(idx))
147}
148
149pub fn pdfdest<ET: EngineTypes>(
150 engine: &mut EngineReferences<ET>,
151 tk: ET::Token,
152) -> TeXResult<(), ET>
153where
154 ET::Extension: PDFExtension<ET>,
155 ET::CustomNode: From<PDFNode<ET>>,
156{
157 let structnum = if engine.read_keyword(b"struct")? {
158 Some(engine.read_int(false, &tk)?.into())
159 } else {
160 None
161 };
162 let id = match super::nodes::num_or_name(engine, &tk)? {
163 Some(n) => n,
164 _ => {
165 TeXError::missing_keyword(engine.aux, engine.state, engine.mouth, &["name", "num"])?;
166 NumOrName::Num(0)
167 }
168 };
169 let dest = super::nodes::pdfdest_type(engine, &tk)?;
170 let node = PDFNode::PDFDest(PDFDest {
171 structnum,
172 id,
173 dest,
174 });
175 crate::add_node!(ET::Stomach;engine,VNode::Custom(node.into()),HNode::Custom(node.into()),MathNode::Custom(node.into()));
176 Ok(())
177}
178
179pub fn pdfstartlink<ET: EngineTypes>(
180 engine: &mut EngineReferences<ET>,
181 tk: ET::Token,
182) -> TeXResult<(), ET>
183where
184 ET::Extension: PDFExtension<ET>,
185 ET::CustomNode: From<PDFNode<ET>>,
186{
187 let mut width = None;
188 let mut height = None;
189 let mut depth = None;
190 loop {
191 match engine.read_keywords(&[b"width", b"height", b"depth"])? {
192 Some(b"width") => width = Some(engine.read_dim(false, &tk)?),
193 Some(b"height") => height = Some(engine.read_dim(false, &tk)?),
194 Some(b"depth") => depth = Some(engine.read_dim(false, &tk)?),
195 _ => break,
196 }
197 }
198 let attr = if engine.read_keyword(b"attr")? {
199 let mut attr = String::new();
200 engine.read_braced_string(true, true, &tk, &mut attr)?;
201 Some(attr)
202 } else {
203 None
204 };
205 let action = super::nodes::action_spec(engine, &tk)?;
206 let node = PDFNode::PDFStartLink(PDFStartLink {
207 width,
208 height,
209 depth,
210 attr,
211 action,
212 });
213 crate::add_node!(ET::Stomach;engine,VNode::Custom(node.into()),HNode::Custom(node.into()),MathNode::Custom(node.into()));
214 Ok(())
215}
216
217pub fn pdfendlink<ET: EngineTypes>(
218 engine: &mut EngineReferences<ET>,
219 _tk: ET::Token,
220) -> TeXResult<(), ET>
221where
222 ET::Extension: PDFExtension<ET>,
223 ET::CustomNode: From<PDFNode<ET>>,
224{
225 crate::add_node!(ET::Stomach;engine,
226 VNode::Custom(PDFNode::PDFEndLink.into()),
227 HNode::Custom(PDFNode::PDFEndLink.into()),
228 MathNode::Custom(PDFNode::PDFEndLink.into())
229 );
230 Ok(())
231}
232
233pub fn pdfsave<ET: EngineTypes>(
234 engine: &mut EngineReferences<ET>,
235 _tk: ET::Token,
236) -> TeXResult<(), ET>
237where
238 ET::Extension: PDFExtension<ET>,
239 ET::CustomNode: From<PDFNode<ET>>,
240{
241 crate::add_node!(ET::Stomach;engine,
242 VNode::Custom(PDFNode::PDFSave.into()),
243 HNode::Custom(PDFNode::PDFSave.into()),
244 MathNode::Custom(PDFNode::PDFSave.into())
245 );
246 Ok(())
247}
248pub fn pdfrestore<ET: EngineTypes>(
249 engine: &mut EngineReferences<ET>,
250 _tk: ET::Token,
251) -> TeXResult<(), ET>
252where
253 ET::Extension: PDFExtension<ET>,
254 ET::CustomNode: From<PDFNode<ET>>,
255{
256 crate::add_node!(ET::Stomach;engine,
257 VNode::Custom(PDFNode::PDFRestore.into()),
258 HNode::Custom(PDFNode::PDFRestore.into()),
259 MathNode::Custom(PDFNode::PDFRestore.into())
260 );
261 Ok(())
262}
263
264pub fn pdfsetmatrix<ET: EngineTypes>(
265 engine: &mut EngineReferences<ET>,
266 tk: ET::Token,
267) -> TeXResult<(), ET>
268where
269 ET::Extension: PDFExtension<ET>,
270 ET::CustomNode: From<PDFNode<ET>>,
271{
272 let mut str = engine.aux.memory.get_string();
273 engine.read_braced_string(true, true, &tk, &mut str)?;
274 let mut scale = 0f32;
275 let mut rotate = 0f32;
276 let mut skewx = 0f32;
277 let mut skewy = 0f32;
278 for (i, s) in str.split(|c: char| c.is_ascii_whitespace()).enumerate() {
279 let f = match s.parse::<f32>() {
280 Ok(f) => f,
281 _ => {
282 engine.general_error(
283 "pdfTeX error (\\pdfsetmatrix): Unrecognized format".to_string(),
284 )?;
285 1.0
286 }
287 };
288 match i {
289 0 => scale = f,
290 1 => rotate = f,
291 2 => skewx = f,
292 3 => skewy = f,
293 _ => engine
294 .general_error("pdfTeX error (\\pdfsetmatrix): Unrecognized format".to_string())?,
295 }
296 }
297 engine.aux.memory.return_string(str);
298 crate::add_node!(ET::Stomach;engine,
299 VNode::Custom(PDFNode::PDFMatrix{scale,rotate,skewx,skewy}.into()),
300 HNode::Custom(PDFNode::PDFMatrix{scale,rotate,skewx,skewy}.into()),
301 MathNode::Custom(PDFNode::PDFMatrix{scale,rotate,skewx,skewy}.into())
302 );
303 Ok(())
304}
305
306pub fn pdfinfo<ET: EngineTypes>(
307 engine: &mut EngineReferences<ET>,
308 tk: ET::Token,
309) -> TeXResult<(), ET> {
310 engine.skip_argument(&tk)
311}
312
313pub fn ifincsname<ET: EngineTypes>(
314 engine: &mut EngineReferences<ET>,
315 _tk: ET::Token,
316) -> TeXResult<bool, ET> {
317 Ok(*engine.gullet.csnames() > 0)
318}
319pub fn ifpdfabsnum<ET: EngineTypes>(
320 engine: &mut EngineReferences<ET>,
321 tk: ET::Token,
322) -> TeXResult<bool, ET> {
323 let first = engine.read_int(false, &tk)?;
324 let rel = match engine.read_chars(b"=<>")? {
325 either::Left(b) => b,
326 _ => {
327 TeXError::missing_keyword(engine.aux, engine.state, engine.mouth, &["=", "<", ">"])?;
328 b'='
329 }
330 };
331 let second = engine.read_int(false, &tk)?;
332
333 let first = if first < <<ET as EngineTypes>::Num as NumSet>::Int::default() {
334 -first
335 } else {
336 first
337 };
338 let second = if second < <<ET as EngineTypes>::Num as NumSet>::Int::default() {
339 -second
340 } else {
341 second
342 };
343 Ok(match rel {
344 b'=' => first == second,
345 b'<' => first < second,
346 b'>' => first > second,
347 _ => unreachable!(),
348 })
349}
350pub fn ifpdfabsdim<ET: EngineTypes>(
351 engine: &mut EngineReferences<ET>,
352 tk: ET::Token,
353) -> TeXResult<bool, ET> {
354 let first = engine.read_dim(false, &tk)?;
355 let rel = match engine.read_chars(b"=<>")? {
356 either::Left(b) => b,
357 _ => {
358 TeXError::missing_keyword(engine.aux, engine.state, engine.mouth, &["=", "<", ">"])?;
359 b'='
360 }
361 };
362 let second = engine.read_dim(false, &tk)?;
363 let first = if first < <<ET as EngineTypes>::Num as NumSet>::Dim::default() {
364 -first
365 } else {
366 first
367 };
368 let second = if second < <<ET as EngineTypes>::Num as NumSet>::Dim::default() {
369 -second
370 } else {
371 second
372 };
373 Ok(match rel {
374 b'=' => first == second,
375 b'<' => first < second,
376 b'>' => first > second,
377 _ => unreachable!(),
378 })
379}
380pub fn ifpdfprimitive<ET: EngineTypes>(
381 engine: &mut EngineReferences<ET>,
382 _tk: ET::Token,
383) -> TeXResult<bool, ET> {
384 use crate::engine::mouth::Mouth;
385 engine.general_error(format!(
386 "Not yet implemented: \\ifpdfprimitive at {}",
387 engine.mouth.current_sourceref().display(engine.filesystem)
388 ))?;
389 Ok(false)
390}
391
392pub fn lpcode_get<ET: EngineTypes>(
393 engine: &mut EngineReferences<ET>,
394 tk: ET::Token,
395) -> TeXResult<ET::Int, ET>
396where
397 ET::Font: FontWithLpRp,
398{
399 let fnt = engine.read_font(false, &tk)?;
400 let char = engine.read_charcode(false, &tk)?;
401 Ok(fnt.get_lp(char))
402}
403pub fn lpcode_set<ET: EngineTypes>(
404 engine: &mut EngineReferences<ET>,
405 tk: ET::Token,
406 _globally: bool,
407) -> TeXResult<(), ET>
408where
409 ET::Font: FontWithLpRp,
410{
411 let mut fnt = engine.read_font(false, &tk)?;
412 let char = engine.read_charcode(false, &tk)?;
413 let code = engine.read_int(true, &tk)?;
414 fnt.set_lp(char, code);
415 Ok(())
416}
417pub fn rpcode_get<ET: EngineTypes>(
418 engine: &mut EngineReferences<ET>,
419 tk: ET::Token,
420) -> TeXResult<ET::Int, ET>
421where
422 ET::Font: FontWithLpRp,
423{
424 let fnt = engine.read_font(false, &tk)?;
425 let char = engine.read_charcode(false, &tk)?;
426 Ok(fnt.get_rp(char))
427}
428pub fn rpcode_set<ET: EngineTypes>(
429 engine: &mut EngineReferences<ET>,
430 tk: ET::Token,
431 _globally: bool,
432) -> TeXResult<(), ET>
433where
434 ET::Font: FontWithLpRp,
435{
436 let mut fnt = engine.read_font(false, &tk)?;
437 let char = engine.read_charcode(false, &tk)?;
438 let code = engine.read_int(true, &tk)?;
439 fnt.set_rp(char, code);
440 Ok(())
441}
442
443pub fn leftmarginkern<ET: EngineTypes>(
444 engine: &mut EngineReferences<ET>,
445 exp: &mut Vec<ET::Token>,
446 tk: ET::Token,
447) -> TeXResult<(), ET> {
448 let _ = engine.read_int(false, &tk)?;
450 Otherize::new(&mut |t| exp.push(t)).write_str("0pt")?;
451 Ok(())
452}
453pub fn rightmarginkern<ET: EngineTypes>(
454 engine: &mut EngineReferences<ET>,
455 exp: &mut Vec<ET::Token>,
456 tk: ET::Token,
457) -> TeXResult<(), ET> {
458 let _ = engine.read_int(false, &tk)?;
460 Otherize::new(&mut |t| exp.push(t)).write_str("0pt")?;
461 Ok(())
462}
463
464pub fn pdfcreationdate<ET: EngineTypes>(
465 engine: &mut EngineReferences<ET>,
466 exp: &mut Vec<ET::Token>,
467 _tk: ET::Token,
468) -> TeXResult<(), ET> {
469 use chrono::{Datelike, Timelike};
470 let dt = engine.aux.start_time;
471 let mut f = |t| exp.push(t);
472 let mut tk = Otherize::new(&mut f);
473 write!(
474 tk,
475 "D:{}{:02}{:02}{:02}{:02}{:02}{}'",
476 dt.year(),
477 dt.month(),
478 dt.day(),
479 dt.hour(),
480 dt.minute(),
481 dt.second(),
482 dt.offset().to_string().replace(':', "'")
483 )?;
484 Ok(())
485}
486
487pub fn pdffilemoddate<ET: EngineTypes>(
488 engine: &mut EngineReferences<ET>,
489 exp: &mut Vec<ET::Token>,
490 tk: ET::Token,
491) -> TeXResult<(), ET> {
492 use chrono::{Datelike, Timelike};
493 let mut filename = engine.aux.memory.get_string();
494 engine.read_braced_string(true, false, &tk, &mut filename)?;
495 let f = engine.filesystem.get(&filename);
496 engine.aux.memory.return_string(filename);
497 let path = f.path();
498 if let Ok(Ok(st)) = std::fs::metadata(path).map(|md| md.modified()) {
499 let dt: chrono::DateTime<chrono::Local> = chrono::DateTime::from(st);
500 let mut f = |t| exp.push(t);
501 let mut tk = Otherize::new(&mut f);
502 write!(
503 tk,
504 "D:{}{:02}{:02}{:02}{:02}{:02}{}'",
505 dt.year(),
506 dt.month(),
507 dt.day(),
508 dt.hour(),
509 dt.minute(),
510 dt.second(),
511 dt.offset().to_string().replace(':', "'")
512 )?;
513 }
514 Ok(())
515}
516
517pub fn pdfescapestring<ET: EngineTypes>(
518 engine: &mut EngineReferences<ET>,
519 exp: &mut Vec<ET::Token>,
520 tk: ET::Token,
521) -> TeXResult<(), ET> {
522 use crate::tex::characters::Character;
523 engine.expand_until_bgroup(false, &tk)?;
524 engine.expand_until_endgroup(true, false, &tk, |_, _, t| {
525 match t.to_enum() {
526 StandardToken::Character(_, CommandCode::Space) => ET::Char::string_to_iter("\\040")
527 .for_each(|c| exp.push(ET::Token::from_char_cat(c, CommandCode::Other))),
528 StandardToken::Character(c, _) if c == ET::Char::from(b' ') => {
529 ET::Char::string_to_iter("\\040")
530 .for_each(|c| exp.push(ET::Token::from_char_cat(c, CommandCode::Other)))
531 }
532 StandardToken::Character(c, _) if c == ET::Char::from(b'(') => {
533 ET::Char::string_to_iter("\\(")
534 .for_each(|c| exp.push(ET::Token::from_char_cat(c, CommandCode::Other)))
535 }
536 StandardToken::Character(c, _) if c == ET::Char::from(b')') => {
537 ET::Char::string_to_iter("\\)")
538 .for_each(|c| exp.push(ET::Token::from_char_cat(c, CommandCode::Other)))
539 }
540 StandardToken::Character(c, _) if c == ET::Char::from(b'\\') => {
541 ET::Char::string_to_iter("\\\\")
542 .for_each(|c| exp.push(ET::Token::from_char_cat(c, CommandCode::Other)))
543 }
544 StandardToken::Character(c, _) if c == ET::Char::from(b'#') => {
545 ET::Char::string_to_iter("\\#")
546 .for_each(|c| exp.push(ET::Token::from_char_cat(c, CommandCode::Other)))
547 }
548 StandardToken::Character(c, _) => {
549 exp.push(ET::Token::from_char_cat(c, CommandCode::Other))
550 }
551 StandardToken::Primitive(_) => (),
552 StandardToken::ControlSequence(_) => (),
553 };
554 Ok(())
555 })?;
556 Ok(())
557}
558
559pub fn pdfescapename<ET: EngineTypes>(
560 engine: &mut EngineReferences<ET>,
561 exp: &mut Vec<ET::Token>,
562 tk: ET::Token,
563) -> TeXResult<(), ET> {
564 use crate::tex::characters::Character;
565 engine.expand_until_bgroup(false, &tk)?;
566 engine.expand_until_endgroup(true, false, &tk, |_, _, t| {
567 match t.to_enum() {
568 StandardToken::Character(_, CommandCode::Space) => ET::Char::string_to_iter("#20")
569 .for_each(|c| exp.push(ET::Token::from_char_cat(c, CommandCode::Other))),
570 StandardToken::Character(c, _) if c == ET::Char::from(b' ') => {
571 ET::Char::string_to_iter("#20")
572 .for_each(|c| exp.push(ET::Token::from_char_cat(c, CommandCode::Other)))
573 }
574 StandardToken::Character(c, _) if c == ET::Char::from(b'(') => {
575 ET::Char::string_to_iter("#28")
576 .for_each(|c| exp.push(ET::Token::from_char_cat(c, CommandCode::Other)))
577 }
578 StandardToken::Character(c, _) if c == ET::Char::from(b')') => {
579 ET::Char::string_to_iter("#29")
580 .for_each(|c| exp.push(ET::Token::from_char_cat(c, CommandCode::Other)))
581 }
582 StandardToken::Character(c, _) if c == ET::Char::from(b'\\') => {
583 ET::Char::string_to_iter("#5C")
584 .for_each(|c| exp.push(ET::Token::from_char_cat(c, CommandCode::Other)))
585 }
586 StandardToken::Character(c, _) if c == ET::Char::from(b'#') => {
587 ET::Char::string_to_iter("#23")
588 .for_each(|c| exp.push(ET::Token::from_char_cat(c, CommandCode::Other)))
589 }
590 StandardToken::Character(c, _) => {
591 exp.push(ET::Token::from_char_cat(c, CommandCode::Other))
592 }
593 StandardToken::Primitive(_) => (),
594 StandardToken::ControlSequence(_) => (),
595 };
596 Ok(())
597 })?;
598 Ok(())
599}
600
601pub fn pdfescapehex<ET: EngineTypes>(
602 engine: &mut EngineReferences<ET>,
603 exp: &mut Vec<ET::Token>,
604 tk: ET::Token,
605) -> TeXResult<(), ET> {
606 use crate::tex::characters::Character;
607 engine.expand_until_bgroup(false, &tk)?;
608 engine.expand_until_endgroup(true, false, &tk, |_, _, t| {
609 match t.to_enum() {
610 StandardToken::Character(c, _) => {
611 let num = c.into();
612 for c in ET::Char::string_to_iter(&format!("{:02X}", num)) {
613 exp.push(ET::Token::from_char_cat(c, CommandCode::Other))
614 }
615 }
616 StandardToken::Primitive(_) => (),
617 StandardToken::ControlSequence(_) => (),
618 };
619 Ok(())
620 })?;
621 Ok(())
622}
623pub fn pdfunescapehex<ET: EngineTypes>(
624 engine: &mut EngineReferences<ET>,
625 exp: &mut Vec<ET::Token>,
626 tk: ET::Token,
627) -> TeXResult<(), ET> {
628 engine.expand_until_bgroup(false, &tk)?;
629 let mut s = String::new();
630 engine.expand_until_endgroup(true, false, &tk, |_, _, t| {
631 match t.to_enum() {
632 StandardToken::Character(c, _) if s.is_empty() => {
633 let num = c.into();
634 if (48..=57).contains(&num) || (65..=70).contains(&num) || (97..=102).contains(&num)
635 {
636 s.push((num as u8).into());
637 } else {
638 }
640 }
641 StandardToken::Character(c, _) => {
642 let num = c.into();
643 if (48..=57).contains(&num) || (65..=70).contains(&num) || (97..=102).contains(&num)
644 {
645 s.push((num as u8).into());
646 } else {
647 }
649 let c = u8::from_str_radix(&s, 16).unwrap();
650 s.clear();
651 exp.push(ET::Token::from_char_cat(c.into(), CommandCode::Other));
652 }
653 StandardToken::Primitive(_) => (),
654 StandardToken::ControlSequence(_) => (),
655 };
656 Ok(())
657 })?;
658 Ok(())
659}
660
661pub fn pdffilesize<ET: EngineTypes>(
662 engine: &mut EngineReferences<ET>,
663 exp: &mut Vec<ET::Token>,
664 tk: ET::Token,
665) -> TeXResult<(), ET> {
666 let mut filename = engine.aux.memory.get_string();
667 engine.read_braced_string(false, true, &tk, &mut filename)?;
668 let file = engine.filesystem.get(&filename);
669 engine.aux.memory.return_string(filename);
670 if file.exists() {
671 let size = file.size();
672 for u in size.to_string().bytes() {
673 exp.push(ET::Token::from_char_cat(u.into(), CommandCode::Other));
674 }
675 }
676 Ok(())
677}
678
679pub fn pdfglyphtounicode<ET: EngineTypes>(
680 engine: &mut EngineReferences<ET>,
681 tk: ET::Token,
682) -> TeXResult<(), ET> {
683 engine.skip_argument(&tk)?;
685 engine.skip_argument(&tk)
686}
687
688pub fn pdfmatch<ET: EngineTypes>(
689 engine: &mut EngineReferences<ET>,
690 exp: &mut Vec<ET::Token>,
691 tk: ET::Token,
692) -> TeXResult<(), ET>
693where
694 ET::Extension: PDFExtension<ET>,
695{
696 let icase = engine.read_keyword(b"icase")?;
697 let _subcount = if engine.read_keyword(b"subcount")? {
698 engine.read_int(false, &tk)?.into()
699 } else {
700 -1
701 }; let mut pattern_string = String::new();
703 let mut target_string = String::new();
704 if icase {
705 pattern_string.push_str("(?i)");
706 }
707 engine.read_braced_string(false, true, &tk, &mut pattern_string)?;
708 engine.read_braced_string(false, true, &tk, &mut target_string)?;
709 let pdfmatches = engine.aux.extension.pdfmatches();
710 pdfmatches.clear();
711
712 match regex::Regex::new(&pattern_string) {
713 Err(_) => {
714 exp.push(ET::Token::from_char_cat(b'-'.into(), CommandCode::Other));
715 exp.push(ET::Token::from_char_cat(b'1'.into(), CommandCode::Other));
716 }
717 Ok(reg) => match reg.captures_iter(&target_string).next() {
718 None => exp.push(ET::Token::from_char_cat(b'0'.into(), CommandCode::Other)),
719 Some(capture) => {
720 let cap = capture.get(0).unwrap();
721 pdfmatches.push(format!("{}->{}", cap.start(), cap.as_str()));
722 for cap in capture.iter().skip(1) {
723 match cap {
724 None => pdfmatches.push("-1".to_string()),
725 Some(cap) => {
726 pdfmatches.push(format!("{}->{}", cap.start(), cap.as_str()));
727 }
728 }
729 }
730 exp.push(ET::Token::from_char_cat(b'1'.into(), CommandCode::Other));
731 }
732 },
733 }
734 Ok(())
735}
736
737pub fn pdflastmatch<ET: EngineTypes>(
738 engine: &mut EngineReferences<ET>,
739 exp: &mut Vec<ET::Token>,
740 tk: ET::Token,
741) -> TeXResult<(), ET>
742where
743 ET::Extension: PDFExtension<ET>,
744{
745 let i = engine.read_int(false, &tk)?.into();
746 let i = if i < 0 { 0usize } else { i as usize };
747 match engine.aux.extension.pdfmatches().get(i) {
748 None => {
749 exp.push(ET::Token::from_char_cat(b'-'.into(), CommandCode::Other));
750 exp.push(ET::Token::from_char_cat(b'1'.into(), CommandCode::Other));
751 }
752 Some(s) => {
753 let mut f = |t| exp.push(t);
754 let mut t = Otherize::new(&mut f);
755 write!(t, "{}", s)?;
756 }
757 }
758 Ok(())
759}
760
761pub fn pdfmdfivesum<ET: EngineTypes>(
762 engine: &mut EngineReferences<ET>,
763 exp: &mut Vec<ET::Token>,
764 tk: ET::Token,
765) -> TeXResult<(), ET>
766where
767 ET::File: FileWithMD5,
768{
769 let mut f = |t| exp.push(t);
770 if engine.read_keyword(b"file")? {
771 let mut filename = String::new();
772 engine.read_braced_string(true, true, &tk, &mut filename)?;
773 let file = engine.filesystem.get(&filename);
774 let mut t = Otherize::new(&mut f);
775 for i in file.md5() {
776 write!(t, "{i:02X}")?;
777 }
778 } else {
779 let mut str = String::new();
780 engine.read_braced_string(false, true, &tk, &mut str)?;
781 let mut t = Otherize::new(&mut f);
782 let mut hasher = md5::Md5::default();
783 hasher.update(str.as_bytes());
784 let r: [u8; 16] = hasher.finalize().into();
785 for i in r {
786 write!(t, "{i:02X}")?;
787 }
788 }
789 Ok(())
790}
791
792pub fn pdfannot<ET: EngineTypes>(
793 engine: &mut EngineReferences<ET>,
794 tk: ET::Token,
795) -> TeXResult<(), ET>
796where
797 ET::Extension: PDFExtension<ET>,
798 ET::CustomNode: From<PDFNode<ET>>,
799{
800 let num = match engine.read_keywords(&[b"reserveobjnum", b"useobjnum"])? {
801 Some(b"reserveobjnum") => {
802 engine.aux.extension.pdfannots().push(PDFAnnot {
803 width: None,
804 height: None,
805 depth: None,
806 content: String::new(),
807 });
808 return Ok(());
809 }
810 Some(b"useobjnum") => {
811 let num = engine.read_int(false, &tk)?.into();
812 if num < 0 {
813 engine.general_error("pdfTeX error (ext1): invalid object number.".to_string())?;
814 }
815 let num = num as usize;
816 if num >= engine.aux.extension.pdfannots().len() {
817 engine.general_error("pdfTeX error (ext1): invalid object number.".to_string())?;
818 }
819 Some(num)
820 }
821 _ => None,
822 };
823 let mut width = None;
824 let mut height = None;
825 let mut depth = None;
826 loop {
827 match engine.read_keywords(&[b"width", b"height", b"depth"])? {
828 Some(b"width") => width = Some(engine.read_dim(false, &tk)?),
829 Some(b"height") => height = Some(engine.read_dim(false, &tk)?),
830 Some(b"depth") => depth = Some(engine.read_dim(false, &tk)?),
831 _ => break,
832 }
833 }
834 let mut content = String::new();
835 engine.read_braced_string(true, true, &tk, &mut content)?;
836 let annot = PDFAnnot {
837 width,
838 height,
839 depth,
840 content,
841 };
842 match num {
843 None => engine.aux.extension.pdfannots().push(annot.clone()),
844 Some(num) => engine.aux.extension.pdfannots()[num] = annot.clone(),
845 }
846 crate::add_node!(ET::Stomach;engine,
847 VNode::Custom(PDFNode::PDFAnnot(annot).into()),
848 HNode::Custom(PDFNode::PDFAnnot(annot).into()),
849 MathNode::Custom(PDFNode::PDFAnnot(annot).into())
850 );
851 Ok(())
852}
853
854pub fn pdflastannot<ET: EngineTypes>(
855 engine: &mut EngineReferences<ET>,
856 _tk: ET::Token,
857) -> TeXResult<<ET::Num as NumSet>::Int, ET>
858where
859 ET::Extension: PDFExtension<ET>,
860{
861 Ok(<ET::Num as NumSet>::Int::from(
862 (engine.aux.extension.pdfannots().len() as i32) - 1,
863 ))
864}
865
866pub fn parse_pdfobj<ET: EngineTypes>(
867 engine: &mut EngineReferences<ET>,
868 tk: &ET::Token,
869) -> TeXResult<usize, ET>
870where
871 ET::Extension: PDFExtension<ET>,
872{
873 match engine.read_keywords(&[b"reserveobjnum", b"useobjnum", b"stream"])? {
874 Some(b"reserveobjnum") => {
875 engine.aux.extension.pdfobjs().push(PDFObj(String::new()));
876 Ok(engine.aux.extension.pdfobjs().len() - 1)
877 }
878 Some(b"useobjnum") => {
879 let num = engine.read_int(false, tk)?.into();
880 if num < 0 {
881 engine.general_error("pdfTeX error (ext1): invalid object number.".to_string())?;
882 }
883 let num = num as usize;
884 if num >= engine.aux.extension.pdfobjs().len() {
885 engine.general_error("pdfTeX error (ext1): invalid object number.".to_string())?;
886 }
887 let mut str = String::new();
888 engine.read_braced_string(false, true, tk, &mut str)?;
889 engine.aux.extension.pdfobjs()[num] = PDFObj(str);
890 Ok(num)
891 }
892 Some(b"stream") => {
893 if engine.read_keyword(b"attr")? {
894 }
896 let mut str = String::new();
897 engine.read_braced_string(false, true, tk, &mut str)?;
898 engine.aux.extension.pdfobjs().push(PDFObj(str));
899 Ok(engine.aux.extension.pdfobjs().len() - 1)
900 }
901 _ => {
902 TeXError::missing_keyword(
903 engine.aux,
904 engine.state,
905 engine.mouth,
906 &["reserveobjnum", "useobjnum", "stream"],
907 )?;
908 Ok(0)
909 }
910 }
911}
912
913pub fn pdfobj<ET: EngineTypes>(
914 engine: &mut EngineReferences<ET>,
915 tk: ET::Token,
916) -> TeXResult<Option<Box<WhatsitFunction<ET>>>, ET>
917where
918 ET::Extension: PDFExtension<ET>,
919{
920 parse_pdfobj(engine, &tk)?;
921 Ok(None)
922}
923pub fn pdfobj_immediate<ET: EngineTypes>(
924 engine: &mut EngineReferences<ET>,
925 tk: ET::Token,
926) -> TeXResult<(), ET>
927where
928 ET::Extension: PDFExtension<ET>,
929 ET::CustomNode: From<PDFNode<ET>>,
930{
931 let num = parse_pdfobj(engine, &tk)?;
932 let node = PDFNode::Obj(engine.aux.extension.pdfobjs()[num].clone());
933 crate::add_node!(ET::Stomach;engine,VNode::Custom(node.into()),HNode::Custom(node.into()),MathNode::Custom(node.into()));
934 Ok(())
935}
936
937pub fn pdfrefobj<ET: EngineTypes>(
938 engine: &mut EngineReferences<ET>,
939 tk: ET::Token,
940) -> TeXResult<(), ET>
941where
942 ET::Extension: PDFExtension<ET>,
943 ET::CustomNode: From<PDFNode<ET>>,
944{
945 let num = engine.read_int(false, &tk)?.into();
946 if num < 0 {
947 engine.general_error("pdfTeX error (ext1): invalid object number.".to_string())?;
948 }
949 match engine.aux.extension.pdfobjs().get(num as usize) {
950 None => {
951 engine.general_error("pdfTeX error (ext1): invalid object number.".to_string())?;
952 }
953 Some(o) => {
954 let node = PDFNode::Obj(o.clone());
955 crate::add_node!(ET::Stomach;engine,VNode::Custom(node.into()),HNode::Custom(node.into()),MathNode::Custom(node.into()))
956 }
957 }
958 Ok(())
959}
960
961pub fn pdflastobj<ET: EngineTypes>(
962 engine: &mut EngineReferences<ET>,
963 _tk: ET::Token,
964) -> TeXResult<ET::Int, ET>
965where
966 ET::Extension: PDFExtension<ET>,
967{
968 Ok(<ET::Num as NumSet>::Int::from(
969 (engine.aux.extension.pdfobjs().len() as i32) - 1,
970 ))
971}
972
973pub fn pdfoutline<ET: EngineTypes>(
974 engine: &mut EngineReferences<ET>,
975 tk: ET::Token,
976) -> TeXResult<(), ET>
977where
978 ET::Extension: PDFExtension<ET>,
979 ET::CustomNode: From<PDFNode<ET>>,
980{
981 let mut attr = String::new();
982 if engine.read_keyword(b"attr")? {
983 engine.read_braced_string(true, true, &tk, &mut attr)?;
984 }
985 let action = super::nodes::action_spec(engine, &tk)?;
986 let count = if engine.read_keyword(b"count")? {
987 Some(engine.read_int(false, &tk)?.into())
988 } else {
989 None
990 };
991 let mut content = String::new();
992 engine.read_braced_string(true, true, &tk, &mut content)?;
993 let node = PDFNode::PDFOutline(PDFOutline {
994 attr,
995 action,
996 count,
997 content,
998 });
999 crate::add_node!(ET::Stomach;engine,VNode::Custom(node.into()),HNode::Custom(node.into()),MathNode::Custom(node.into()));
1000 Ok(())
1001}
1002
1003pub fn parse_pdfxform<ET: EngineTypes>(
1004 engine: &mut EngineReferences<ET>,
1005 tk: &ET::Token,
1006) -> TeXResult<usize, ET>
1007where
1008 ET::Extension: PDFExtension<ET>,
1009{
1010 let mut attr = String::new();
1011 if engine.read_keyword(b"attr")? {
1012 engine.read_braced_string(true, true, tk, &mut attr)?;
1013 }
1014 let mut resources = String::new();
1015 if engine.read_keyword(b"resources")? {
1016 engine.read_braced_string(true, true, tk, &mut resources)?;
1017 }
1018 let idx = engine.read_register_index(false, tk)?;
1019 let bx = engine.state.take_box_register(idx);
1020 engine.aux.extension.pdfxforms().push(PDFXForm {
1021 attr,
1022 resources,
1023 bx,
1024 });
1025 Ok(engine.aux.extension.pdfxforms().len() - 1)
1026}
1027pub fn pdfxform<ET: EngineTypes>(
1028 engine: &mut EngineReferences<ET>,
1029 tk: ET::Token,
1030) -> TeXResult<Option<Box<WhatsitFunction<ET>>>, ET>
1031where
1032 ET::Extension: PDFExtension<ET>,
1033{
1034 parse_pdfxform(engine, &tk)?;
1035 Ok(None)
1036}
1037pub fn pdfxform_immediate<ET: EngineTypes>(
1038 engine: &mut EngineReferences<ET>,
1039 tk: ET::Token,
1040) -> TeXResult<(), ET>
1041where
1042 ET::Extension: PDFExtension<ET>,
1043 ET::CustomNode: From<PDFNode<ET>>,
1044{
1045 let num = parse_pdfxform(engine, &tk)?;
1046 let form = engine.aux.extension.pdfxforms()[num].clone();
1047 let node = PDFNode::XForm(form);
1048 crate::add_node!(ET::Stomach;engine,VNode::Custom(node.into()),HNode::Custom(node.into()),MathNode::Custom(node.into()));
1049 Ok(())
1050}
1051
1052pub fn pdfrefxform<ET: EngineTypes>(
1053 engine: &mut EngineReferences<ET>,
1054 tk: ET::Token,
1055) -> TeXResult<(), ET>
1056where
1057 ET::Extension: PDFExtension<ET>,
1058 ET::CustomNode: From<PDFNode<ET>>,
1059{
1060 let num = engine.read_int(false, &tk)?.into();
1061 if num < 0 {
1062 engine.general_error("pdfTeX error (ext1): invalid object number.".to_string())?;
1063 }
1064 match engine.aux.extension.pdfxforms().get(num as usize) {
1065 None => {
1066 engine.general_error("pdfTeX error (ext1): invalid object number.".to_string())?;
1067 }
1068 Some(n) => {
1069 let node = PDFNode::XForm(n.clone());
1070 crate::add_node!(ET::Stomach;engine,VNode::Custom(node.into()),HNode::Custom(node.into()),MathNode::Custom(node.into()))
1071 }
1072 }
1073 Ok(())
1074}
1075
1076pub fn pdflastxform<ET: EngineTypes>(
1077 engine: &mut EngineReferences<ET>,
1078 _tk: ET::Token,
1079) -> TeXResult<ET::Int, ET>
1080where
1081 ET::Extension: PDFExtension<ET>,
1082{
1083 Ok(<ET::Num as NumSet>::Int::from(
1084 (engine.aux.extension.pdfxforms().len() as i32) - 1,
1085 ))
1086}
1087
1088pub fn pdfximage<ET: EngineTypes>(
1089 engine: &mut EngineReferences<ET>,
1090 tk: ET::Token,
1091) -> TeXResult<(), ET>
1092where
1093 ET::Extension: PDFExtension<ET>,
1094{
1095 let mut width: Option<ET::Dim> = None;
1096 let mut height: Option<ET::Dim> = None;
1097 let mut depth: Option<ET::Dim> = None;
1098 loop {
1099 match engine.read_keywords(&[b"width", b"height", b"depth"])? {
1100 Some(b"width") => width = Some(engine.read_dim(false, &tk)?),
1101 Some(b"height") => height = Some(engine.read_dim(false, &tk)?),
1102 Some(b"depth") => depth = Some(engine.read_dim(false, &tk)?),
1103 _ => break,
1104 }
1105 }
1106 let mut attr = String::new();
1107 if engine.read_keyword(b"attr")? {
1108 engine.read_braced_string(true, true, &tk, &mut attr)?
1109 }
1110 let page = if engine.read_keyword(b"page")? {
1111 Some(engine.read_int(false, &tk)?.into())
1112 } else {
1113 None
1114 };
1115 let colorspace = if engine.read_keyword(b"colorspace")? {
1116 Some(engine.read_int(false, &tk)?.into())
1117 } else {
1118 None
1119 };
1120 let boxspec = match engine.read_keywords(&[
1121 b"mediabox",
1122 b"cropbox",
1123 b"bleedbox",
1124 b"trimbox",
1125 b"artbox",
1126 ])? {
1127 None => None,
1128 Some(b"mediabox") => Some(PDFBoxSpec::MediaBox),
1129 Some(b"cropbox") => Some(PDFBoxSpec::CropBox),
1130 Some(b"bleedbox") => Some(PDFBoxSpec::BleedBox),
1131 Some(b"trimbox") => Some(PDFBoxSpec::TrimBox),
1132 Some(b"artbox") => Some(PDFBoxSpec::ArtBox),
1133 _ => unreachable!(),
1134 };
1135 let mut filename = String::new();
1136 engine.read_braced_string(true, true, &tk, &mut filename)?;
1137 let file = engine.filesystem.get(&filename);
1138
1139 let img = if file.path().extension().is_some_and(|ext| ext == "pdf") {
1140 super::nodes::pdf_as_image(file.path(), &mut engine.aux.extension)
1141 } else {
1142 let Ok(img) = image::ImageReader::open(file.path()) else {
1143 engine.general_error("Unknown type of image".into())?;
1144 return Ok(());
1145 };
1146 let Ok(Ok(img)) = img.with_guessed_format().map(image::ImageReader::decode) else {
1147 engine.general_error("Unknown type of image".into())?;
1148 return Ok(());
1149 };
1150 PDFImage::Img(img)
1151 };
1152 let img = PDFXImage {
1153 width,
1154 height,
1155 depth,
1156 attr,
1157 page,
1158 colorspace,
1159 boxspec,
1160 img,
1161 filepath: file.path().to_path_buf(),
1162 };
1163 engine.aux.extension.pdfximages().push(img);
1164 Ok(())
1165}
1166
1167pub fn pdfrefximage<ET: EngineTypes>(
1168 engine: &mut EngineReferences<ET>,
1169 tk: ET::Token,
1170) -> TeXResult<(), ET>
1171where
1172 ET::Extension: PDFExtension<ET>,
1173 ET::CustomNode: From<PDFNode<ET>>,
1174{
1175 let num = engine.read_int(false, &tk)?.into();
1176 if num < 0 {
1177 engine.general_error("pdfTeX error (ext1): invalid object number.".to_string())?;
1178 }
1179 match engine.aux.extension.pdfximages().get(num as usize) {
1180 None => {
1181 engine.general_error("pdfTeX error (ext1): invalid object number.".to_string())?;
1182 }
1183 Some(n) => {
1184 let node = PDFNode::XImage(n.clone());
1185 crate::add_node!(ET::Stomach;engine,VNode::Custom(node.into()),HNode::Custom(node.into()),MathNode::Custom(node.into()))
1186 }
1187 }
1188 Ok(())
1189}
1190
1191pub fn pdfpageattr<ET: EngineTypes>(
1192 engine: &mut EngineReferences<ET>,
1193 tk: ET::Token,
1194) -> TeXResult<(), ET>
1195where
1196 ET::Extension: PDFExtension<ET>,
1197 ET::CustomNode: From<PDFNode<ET>>,
1198{
1199 engine.expand_until_bgroup(false, &tk)?;
1200 let mut v = Vec::new();
1201 engine.read_until_endgroup(&tk, |_, _, t| {
1202 v.push(t);
1203 Ok(())
1204 })?;
1205 let node = PDFNode::PDFPageAttr(v.into());
1206 crate::add_node!(ET::Stomach;engine,VNode::Custom(node.into()),HNode::Custom(node.into()),MathNode::Custom(node.into()));
1207 Ok(())
1208}
1209
1210pub fn pdfpagesattr<ET: EngineTypes>(
1211 engine: &mut EngineReferences<ET>,
1212 tk: ET::Token,
1213) -> TeXResult<(), ET>
1214where
1215 ET::Extension: PDFExtension<ET>,
1216 ET::CustomNode: From<PDFNode<ET>>,
1217{
1218 engine.expand_until_bgroup(false, &tk)?;
1219 let mut v = Vec::new();
1220 engine.read_until_endgroup(&tk, |_, _, t| {
1221 v.push(t);
1222 Ok(())
1223 })?;
1224 let node = PDFNode::PDFPagesAttr(v.into());
1225 crate::add_node!(ET::Stomach;engine,VNode::Custom(node.into()),HNode::Custom(node.into()),MathNode::Custom(node.into()));
1226 Ok(())
1227}
1228
1229pub fn pdfprimitive<ET: EngineTypes>(
1230 engine: &mut EngineReferences<ET>,
1231 _tk: ET::Token,
1232) -> TeXResult<(), ET> {
1233 let name = engine.read_csname()?;
1234 let s = engine
1235 .aux
1236 .memory
1237 .cs_interner_mut()
1238 .resolve(&name)
1239 .to_string();
1240 match engine.state.primitives().get_name(&s) {
1241 None => (),
1242 Some(s) => engine.requeue(ET::Token::primitive(s))?,
1243 }
1244 Ok(())
1245}
1246
1247pub fn pdflastximage<ET: EngineTypes>(
1248 engine: &mut EngineReferences<ET>,
1249 _tk: ET::Token,
1250) -> TeXResult<ET::Int, ET>
1251where
1252 ET::Extension: PDFExtension<ET>,
1253{
1254 Ok((engine.aux.extension.pdfximages().len() as i32 - 1).into())
1255}
1256
1257pub fn pdfliteral<ET: EngineTypes>(
1258 engine: &mut EngineReferences<ET>,
1259 tk: ET::Token,
1260) -> TeXResult<(), ET>
1261where
1262 ET::Extension: PDFExtension<ET>,
1263 ET::CustomNode: From<PDFNode<ET>>,
1264{
1265 let _ = engine.read_keyword(b"shipout")?; let option = match engine.read_keywords(&[b"direct", b"page"])? {
1267 Some(b"direct") => PDFLiteralOption::Direct,
1268 Some(b"page") => PDFLiteralOption::Page,
1269 _ => PDFLiteralOption::None,
1270 };
1271 let mut literal = String::new();
1272 engine.read_braced_string(true, true, &tk, &mut literal)?;
1273 let node = PDFNode::PDFLiteral(PDFLiteral { literal, option });
1274 crate::add_node!(ET::Stomach;engine,VNode::Custom(node.into()),HNode::Custom(node.into()),MathNode::Custom(node.into()));
1275 Ok(())
1276}
1277
1278pub fn pdfshellescape<ET: EngineTypes>(
1279 _engine: &mut EngineReferences<ET>,
1280 _tk: ET::Token,
1281) -> TeXResult<ET::Int, ET> {
1282 Ok(<ET::Num as NumSet>::Int::from(2))
1283}
1284
1285pub fn pdfstrcmp<ET: EngineTypes>(
1286 engine: &mut EngineReferences<ET>,
1287 exp: &mut Vec<ET::Token>,
1288 tk: ET::Token,
1289) -> TeXResult<(), ET> {
1290 let mut first = String::new();
1291 let mut second = String::new();
1292 engine.read_braced_string(false, true, &tk, &mut first)?;
1293 engine.read_braced_string(false, true, &tk, &mut second)?;
1294 match first.cmp(&second) {
1295 std::cmp::Ordering::Less => {
1296 exp.push(ET::Token::from_char_cat(b'-'.into(), CommandCode::Other));
1297 exp.push(ET::Token::from_char_cat(b'1'.into(), CommandCode::Other));
1298 }
1299 std::cmp::Ordering::Equal => {
1300 exp.push(ET::Token::from_char_cat(b'0'.into(), CommandCode::Other));
1301 }
1302 std::cmp::Ordering::Greater => {
1303 exp.push(ET::Token::from_char_cat(b'1'.into(), CommandCode::Other));
1304 }
1305 }
1306 Ok(())
1307}
1308
1309pub fn pdffontsize<ET: EngineTypes>(
1310 engine: &mut EngineReferences<ET>,
1311 exp: &mut Vec<ET::Token>,
1312 tk: ET::Token,
1313) -> TeXResult<(), ET> {
1314 let dim = engine.read_font(false, &tk)?.get_at();
1315 let mut f = |t| exp.push(t);
1316 let mut t = Otherize::new(&mut f);
1317 write!(t, "{}", dim)?;
1318 Ok(())
1319}
1320
1321pub fn showstream<ET: EngineTypes>(
1323 engine: &mut EngineReferences<ET>,
1324 tk: ET::Token,
1325) -> TeXResult<Option<Box<WhatsitFunction<ET>>>, ET>
1326where
1327 ET::Extension: PDFExtension<ET>,
1328 ET::CustomNode: From<PDFNode<ET>>,
1329{
1330 showstream_immediate::<ET>(engine, tk)?;
1331 Ok(None)
1332}
1333pub fn showstream_immediate<ET: EngineTypes>(
1334 engine: &mut EngineReferences<ET>,
1335 tk: ET::Token,
1336) -> TeXResult<(), ET>
1337where
1338 ET::Extension: PDFExtension<ET>,
1339 ET::CustomNode: From<PDFNode<ET>>,
1340{
1341 if engine
1342 .need_next(false, &tk)?
1343 .char_value()
1344 .map(|v| v.to_char())
1345 == Some('=')
1346 {
1347 let _ = engine.need_next(false, &tk)?;
1348 }
1349 Ok(())
1350}
1351
1352pub fn pdffontexpand<ET: EngineTypes>(
1353 engine: &mut EngineReferences<ET>,
1354 tk: ET::Token,
1355) -> TeXResult<(), ET> {
1356 let _ = engine.read_font(false, &tk)?;
1358 let _ = engine.read_int(false, &tk)?;
1359 let _ = engine.read_int(false, &tk)?;
1360 let _ = engine.read_int(false, &tk)?;
1361 engine.read_keyword(b"autoexpand")?;
1362 Ok(())
1363}
1364
1365const PRIMITIVE_INTS: &[&str] = &[
1366 "pdfadjustspacing",
1367 "pdfcompresslevel",
1368 "pdfdecimaldigits",
1369 "pdfdraftmode",
1370 "pdfgentounicode",
1371 "pdfminorversion",
1372 "pdfobjcompresslevel",
1373 "pdfoutput",
1374 "pdfpkresolution",
1375 "pdfprotrudechars",
1376 "tracingstacklevels",
1377 "pdfprependkern",
1378 "pdfappendkern",
1379];
1380
1381const PRIMITIVE_DIMS: &[&str] = &[
1382 "pdfhorigin",
1383 "pdflinkmargin",
1384 "pdfpageheight",
1385 "pdfpagewidth",
1386 "pdfvorigin",
1387];
1388
1389const PRIMITIVE_TOKS: &[&str] = &["pdfpageresources"];
1390
1391pub fn register_pdftex_primitives<E: TeXEngine>(engine: &mut E)
1392where
1393 <E::Types as EngineTypes>::Extension: PDFExtension<E::Types>,
1394 <E::Types as EngineTypes>::CustomNode: From<PDFNode<E::Types>>,
1395 <E::Types as EngineTypes>::File: FileWithMD5,
1396 <E::Types as EngineTypes>::Font: FontWithLpRp,
1397{
1398 register_expandable(engine, "leftmarginkern", leftmarginkern);
1399 register_expandable(engine, "rightmarginkern", rightmarginkern);
1400 register_expandable(engine, "pdfcreationdate", pdfcreationdate);
1401 register_expandable(engine, "pdffilemoddate", pdffilemoddate);
1402 register_expandable(engine, "pdfescapestring", pdfescapestring);
1403 register_expandable(engine, "pdfescapename", pdfescapename);
1404 register_expandable(engine, "pdfescapehex", pdfescapehex);
1405 register_expandable(engine, "pdfunescapehex", pdfunescapehex);
1406 register_expandable(engine, "pdffilesize", pdffilesize);
1407 register_expandable(engine, "pdfmatch", pdfmatch);
1408 register_expandable(engine, "pdflastmatch", pdflastmatch);
1409 register_expandable(engine, "pdfstrcmp", pdfstrcmp);
1410 register_expandable(engine, "pdftexrevision", pdftexrevision);
1411 register_expandable(engine, "pdfmdfivesum", pdfmdfivesum);
1412 register_expandable(engine, "pdffontsize", pdffontsize);
1413
1414 register_int(engine, "pdftexversion", pdftexversion, None);
1415 register_int(engine, "pdfmajorversion", pdfmajorversion, None);
1416 register_int(engine, "pdfshellescape", pdfshellescape, None);
1417 register_int(engine, "pdfcolorstackinit", pdfcolorstackinit, None);
1418 register_int(engine, "lpcode", lpcode_get, Some(lpcode_set));
1419 register_int(engine, "rpcode", rpcode_get, Some(rpcode_set));
1420
1421 register_conditional(engine, "ifincsname", ifincsname);
1422 register_conditional(engine, "ifpdfabsdim", ifpdfabsdim);
1423 register_conditional(engine, "ifpdfabsnum", ifpdfabsnum);
1424 register_conditional(engine, "ifpdfprimitive", ifpdfprimitive);
1425
1426 register_unexpandable(engine, "pdfcatalog", CommandScope::Any, pdfcatalog);
1427 register_unexpandable(
1428 engine,
1429 "pdfglyphtounicode",
1430 CommandScope::Any,
1431 pdfglyphtounicode,
1432 );
1433 register_unexpandable(engine, "pdfcolorstack", CommandScope::Any, pdfcolorstack);
1434 register_unexpandable(engine, "pdfdest", CommandScope::Any, pdfdest);
1435 register_unexpandable(engine, "pdfinfo", CommandScope::Any, pdfinfo);
1436 register_unexpandable(engine, "pdfliteral", CommandScope::Any, pdfliteral);
1437 register_unexpandable(engine, "pdffontexpand", CommandScope::Any, pdffontexpand);
1438 register_unexpandable(engine, "pdfoutline", CommandScope::Any, pdfoutline);
1439 register_unexpandable(engine, "pdfstartlink", CommandScope::Any, pdfstartlink);
1440 register_unexpandable(engine, "pdfendlink", CommandScope::Any, pdfendlink);
1441 register_unexpandable(engine, "pdfsave", CommandScope::Any, pdfsave);
1442 register_unexpandable(engine, "pdfrestore", CommandScope::Any, pdfrestore);
1443 register_unexpandable(engine, "pdfsetmatrix", CommandScope::Any, pdfsetmatrix);
1444 register_unexpandable(engine, "pdfannot", CommandScope::Any, pdfannot);
1445
1446 register_whatsit(engine, "showstream", showstream, showstream_immediate, None);
1447 register_whatsit(engine, "pdfobj", pdfobj, pdfobj_immediate, None);
1448 register_whatsit(engine, "pdfxform", pdfxform, pdfxform_immediate, None);
1449 register_whatsit(
1450 engine,
1451 "pdfpageattr",
1452 |e, t| {
1453 pdfpageattr(e, t)?;
1454 Ok(None)
1455 },
1456 pdfpageattr,
1457 Some(|_, _| Ok(Vec::new())),
1458 );
1459 register_whatsit(
1460 engine,
1461 "pdfpagesattr",
1462 |e, t| {
1463 pdfpagesattr(e, t)?;
1464 Ok(None)
1465 },
1466 pdfpagesattr,
1467 Some(|_, _| Ok(Vec::new())),
1468 );
1469 register_unexpandable(engine, "pdfrefobj", CommandScope::Any, pdfrefobj);
1470 register_int(engine, "pdflastobj", pdflastobj, None);
1471 register_unexpandable(engine, "pdfrefxform", CommandScope::Any, pdfrefxform);
1472 register_int(engine, "pdflastxform", pdflastxform, None);
1473 register_unexpandable(engine, "pdfximage", CommandScope::Any, pdfximage);
1474 register_unexpandable(engine, "pdfrefximage", CommandScope::Any, pdfrefximage);
1475 register_simple_expandable(engine, "pdfprimitive", pdfprimitive);
1476 register_int(engine, "pdflastximage", pdflastximage, None);
1477 register_int(engine, "pdflastannot", pdflastannot, None);
1478 register_simple_expandable(engine, "pdfsavepos", |_, _| Ok(()));
1479 register_int(
1480 engine,
1481 "pdflastxpos",
1482 |_, _| Ok(<E::Types as EngineTypes>::Int::default()),
1483 None,
1484 );
1485 register_int(
1486 engine,
1487 "pdflastypos",
1488 |_, _| Ok(<E::Types as EngineTypes>::Int::default()),
1489 None,
1490 );
1491
1492 register_primitive_int(engine, PRIMITIVE_INTS);
1493 register_primitive_dim(engine, PRIMITIVE_DIMS);
1494 register_primitive_toks(engine, PRIMITIVE_TOKS);
1495
1496 cmtodos!(engine, pdfelapsedtime, pdfresettimer);
1497
1498 cmtodo!(engine, efcode);
1499 cmtodo!(engine, knaccode);
1500 cmtodo!(engine, knbccode);
1501 cmtodo!(engine, knbscode);
1502 cmtodo!(engine, pdfadjustinterwordglue);
1503 cmtodo!(engine, pdfforcepagebox);
1504 cmtodo!(engine, pdfgamma);
1505 cmtodo!(engine, pdfimageapplygamma);
1506 cmtodo!(engine, pdfimagegamma);
1507 cmtodo!(engine, pdfimagehicolor);
1508 cmtodo!(engine, pdfimageresolution);
1509 cmtodo!(engine, pdfinclusioncopyfonts);
1510 cmtodo!(engine, pdfinclusionerrorlevel);
1511 cmtodo!(engine, pdfinfoomitdate);
1512 cmtodo!(engine, pdfomitcharset);
1513 cmtodo!(engine, pdfomitinfodict);
1514 cmtodo!(engine, pdfomitprocset);
1515 cmtodo!(engine, pdfpagebox);
1516 cmtodo!(engine, pdfsuppressptexinfo);
1517 cmtodo!(engine, pdfsuppresswarningdupdest);
1518 cmtodo!(engine, pdfsuppresswarningdupmap);
1519 cmtodo!(engine, pdfsuppresswarningpagegroup);
1520 cmtodo!(engine, pdftracingfonts);
1521 cmtodo!(engine, pdfuniqueresname);
1522 cmtodo!(engine, shbscode);
1523 cmtodo!(engine, stbscode);
1524 cmtodo!(engine, tagcode);
1525 cmtodo!(engine, pdflastlink);
1526 cmtodo!(engine, pdflastximagecolordepth);
1527 cmtodo!(engine, pdflastximagepages);
1528 cmtodo!(engine, pdfrandomseed);
1529 cmtodo!(engine, pdfretval);
1530 cmtodo!(engine, pdfdestmargin);
1531 cmtodo!(engine, pdfeachlinedepth);
1532 cmtodo!(engine, pdfeachlineheight);
1533 cmtodo!(engine, pdffirstlineheight);
1534 cmtodo!(engine, pdfignoreddimen);
1535 cmtodo!(engine, pdflastlinedepth);
1536 cmtodo!(engine, pdfpxdimen);
1537 cmtodo!(engine, pdfthreadmargin);
1538 cmtodo!(engine, pdfpkmode);
1539 cmtodo!(engine, pdffiledump);
1540 cmtodo!(engine, pdffontname);
1541 cmtodo!(engine, pdffontobjnum);
1542 cmtodo!(engine, pdfincludechars);
1543 cmtodo!(engine, pdfinsertht);
1544 cmtodo!(engine, pdfnormaldeviate);
1545 cmtodo!(engine, pdfpageref);
1546 cmtodo!(engine, pdftexbanner);
1547 cmtodo!(engine, pdfuniformdeviate);
1548 cmtodo!(engine, pdfxformname);
1549 cmtodo!(engine, pdfximagebbox);
1550
1551 cmtodo!(engine, letterspacefont);
1552 cmtodo!(engine, partokenname);
1553 cmtodo!(engine, pdfcopyfont);
1554 cmtodo!(engine, pdfendthread);
1555 cmtodo!(engine, pdffakespace);
1556 cmtodo!(engine, pdffontattr);
1557 cmtodo!(engine, pdfinterwordspaceoff);
1558 cmtodo!(engine, pdfinterwordspaceon);
1559 cmtodo!(engine, pdfmapfile);
1560 cmtodo!(engine, pdfmapline);
1561 cmtodo!(engine, pdfnames);
1562 cmtodo!(engine, pdfnobuiltintounicode);
1563 cmtodo!(engine, pdfnoligatures);
1564 cmtodo!(engine, pdfrunninglinkoff);
1565 cmtodo!(engine, pdfrunninglinkon);
1566 cmtodo!(engine, pdfsetrandomseed);
1567 cmtodo!(engine, pdfspacefont);
1568 cmtodo!(engine, pdfthread);
1569 cmtodo!(engine, pdftrailer);
1570 cmtodo!(engine, pdftrailerid);
1571 cmtodo!(engine, pdfstartthread);
1572 cmtodo!(engine, quitvmode);
1573
1574 }