1use crate::Engine;
12use crate::glue::Glue;
13use crate::nodes::{NodeData, NodeRef, RUNNING, Whatsit, glue_sub, id};
14use crate::primitives_gen::{assign_dimen as dp, assign_int as ip};
15
16pub trait Backend {
19 fn begin_page(&mut self, engine: &Engine, width: i32, height: i32);
21 fn glyph(&mut self, engine: &Engine, font: u32, chr: u32, x: i32, y: i32);
23 fn glyph_expanded(&mut self, engine: &Engine, font: u32, chr: u32, x: i32, y: i32, ef: i32) {
26 let _ = ef;
27 self.glyph(engine, font, chr, x, y);
28 }
29 fn rule(&mut self, x: i32, y: i32, width: i32, height: i32);
31 fn literal(&mut self, mode: u8, data: &[u8], x: i32, y: i32);
33 fn end_page(&mut self, engine: &Engine);
35 fn finish(&mut self, engine: &mut Engine) -> Option<Finished>;
37
38 fn save(&mut self, x: i32, y: i32) {
40 self.literal(0, b"q", x, y);
41 }
42
43 fn restore(&mut self, x: i32, y: i32) {
45 self.literal(0, b"Q", x, y);
46 }
47
48 fn set_matrix(&mut self, x: i32, y: i32, matrix: &[u8]) {
50 let mut d = matrix.to_vec();
51 d.extend_from_slice(b" 0 0 cm");
52 self.literal(0, &d, x, y);
53 }
54
55 fn suspend(&mut self) -> std::io::Result<()> {
58 Ok(())
59 }
60
61 fn resume(&mut self) -> std::io::Result<()> {
65 Ok(())
66 }
67
68 fn abandon(&mut self, _remove: bool) {}
71
72 fn begin_object(&mut self, _engine: &Engine) {}
75
76 fn current_page_object(&self) -> Option<i32> {
78 None
79 }
80
81 fn close_page_contents(&mut self, _engine: &Engine, _items: &crate::pdfext::PageItems) {}
84
85 fn page_items(&mut self, _engine: &Engine, _items: &crate::pdfext::PageItems) {}
88
89 fn begin_box_form(
93 &mut self,
94 _engine: &Engine,
95 _index: u32,
96 _size: (i32, i32, i32),
97 _attr: &[u8],
98 _resources: &[u8],
99 ) {
100 }
101
102 fn end_box_form(&mut self, _engine: &Engine) {}
104
105 fn close_box_form_resources(&mut self, _engine: &Engine) {}
108
109 fn close_page_resources(&mut self, _engine: &Engine) {}
112
113 fn box_form(
116 &mut self,
117 _engine: &Engine,
118 _index: u32,
119 _x: i32,
120 _y: i32,
121 _width: i32,
122 _height: i32,
123 ) {
124 }
125
126 fn write_image(&mut self, _engine: &Engine, _index: u32) {}
128
129 fn image(
132 &mut self,
133 _engine: &Engine,
134 _index: u32,
135 _x: i32,
136 _y: i32,
137 _width: i32,
138 _height: i32,
139 ) {
140 }
141
142 fn written_images(&mut self) -> Option<Vec<u32>> {
146 None
147 }
148
149 fn write_object(&mut self, _engine: &Engine, _obj: &crate::pdfext::PdfObject) {}
151}
152
153#[derive(Clone, Debug, Default)]
155pub struct Finished {
156 pub file: String,
158 pub size: u64,
160 pub embedded: Vec<String>,
162 pub objstm: (u32, u32),
164}
165
166struct GlueState {
168 cur_g: i32,
169 cur_glue: f64,
170 order: u8,
171 sign: u8,
172 set: f64,
173}
174
175impl GlueState {
176 fn advance(&mut self, g: &Glue) -> i32 {
178 let mut w = g.width - self.cur_g;
179 if self.sign != 0 {
180 let applies = if self.sign == 1 {
181 g.stretch_order == self.order
182 } else {
183 g.shrink_order == self.order
184 };
185 if applies {
186 if self.sign == 1 {
187 self.cur_glue += f64::from(g.stretch);
188 } else {
189 self.cur_glue -= f64::from(g.shrink);
190 }
191 let t = (self.set * self.cur_glue).clamp(-1_000_000_000.0, 1_000_000_000.0);
192 self.cur_g = t.round() as i32;
193 }
194 }
195 w += self.cur_g;
196 w
197 }
198}
199
200impl Engine {
201 fn page_number_callback(&mut self, name: &str) -> bool {
204 let Some(lua) = self.lua.clone() else {
205 return false;
206 };
207 if !lua.has_callback(name) {
208 return false;
209 }
210 if let Err(e) = lua.call_simple(self, name) {
211 self.lua_error(&e);
212 }
213 true
214 }
215
216 pub fn ship_out(&mut self, p: NodeRef) {
218 self.pdf.page_annots.clear();
219 let tracing = self.eqtb.int(ip::TRACINGOUTPUT) > 0;
220 if tracing {
221 self.printer.print_nl("");
222 self.printer.print_ln();
223 self.printer.print_str("Completed box being shipped out");
224 }
225 if !self.page_number_callback("start_page_number") {
228 if self.printer.term_offset > self.printer.max_print_line.saturating_sub(9) {
229 self.printer.print_ln();
230 } else if self.printer.term_offset > 0 || self.printer.file_offset > 0 {
231 self.printer.print_char(' ' as u32);
232 }
233 self.printer.print_char('[' as u32);
234 let mut j = 9;
235 while j > 0 && self.eqtb.count[j].val == 0 {
236 j -= 1;
237 }
238 for k in 0..=j {
239 self.printer.print_int(self.eqtb.count[k].val.into());
240 if k < j {
241 self.printer.print_char('.' as u32);
242 }
243 }
244 }
245 self.printer.update_terminal();
246 if p.is_null() {
247 self.luatex_warning(
249 "backend",
250 "pagedir differs from bodydir, the output may be placed wrongly on the page",
251 );
252 let msg = "no vlist or hlist in (xform) shipout";
253 if self.eqtb.int(ip::OUTPUTMODE) > 0 {
254 self.pdf_fatal(msg);
255 }
256 self.luatex_fatal("pdf backend", msg);
257 }
258 if tracing {
259 if !self.page_number_callback("stop_page_number") {
260 self.printer.print_char(']' as u32);
261 }
262 self.begin_diagnostic();
263 self.show_box(p);
264 self.end_diagnostic(true);
265 }
266 self.ship_box(p);
267 if !tracing && !self.page_number_callback("stop_page_number") {
268 self.printer.print_char(']' as u32);
269 }
270 self.dead_cycles = 0;
271 self.printer.update_terminal();
272 self.nodes.flush_list(p);
273 }
274
275 fn ship_box(&mut self, p: NodeRef) {
277 let Some(b) = self.nodes.box_data(p).cloned() else {
278 return;
279 };
280 let pdf = self.eqtb.int(ip::OUTPUTMODE) > 0;
281 let h_origin = self.eqtb.dimen(crate::backend::H_ORIGIN);
283 let v_origin = self.eqtb.dimen(crate::backend::V_ORIGIN);
284 let h_off = self.eqtb.dimen(dp::HOFFSET);
285 let v_off = self.eqtb.dimen(dp::VOFFSET);
286 let mut width = self.eqtb.dimen(dp::PAGEWIDTH);
287 let mut height = self.eqtb.dimen(dp::PAGEHEIGHT);
288 const ONE_INCH: i32 = 4_736_286;
291 if width <= 0 {
292 width = b.width + 2 * (ONE_INCH + h_off);
293 }
294 if height <= 0 {
295 height = b.height + b.depth + 2 * (ONE_INCH + v_off);
296 }
297 let mut backend = if pdf { self.backend.take() } else { None };
298 let forms_depth = self.pending_forms.len();
299 self.pending_forms.push(Vec::new());
300 if let Some(be) = backend.as_mut() {
301 self.pdf.page = Default::default();
302 be.begin_page(self, width, height);
303 if let Some(n) = be.current_page_object() {
304 let page = self.total_pages as i32 + 1;
305 self.pdf.page_refs.insert(page, n);
306 self.pdf.kinds.insert(n, crate::pdfext::ObjKind::Page);
307 }
308 let (x, y) = (h_origin + h_off, height - (v_origin + v_off + b.height));
310 for (mode, lit) in self.page_colorstack_literals() {
311 be.literal(mode, &lit, x, y);
312 }
313 }
314 let dvi = if pdf {
315 None
316 } else {
317 let mut d = std::mem::take(&mut self.dvi);
318 d.begin_page(self, (b.width, b.height, b.depth), (h_off, v_off));
319 Some(d)
320 };
321 let sync = self.synctex_recording();
322 if sync {
323 self.synctex_begin_page((h_origin + h_off, v_origin + v_off));
324 }
325 let mut walker = Walker {
326 backend,
327 dvi,
328 h_origin: h_origin + h_off,
329 v_origin: v_origin + v_off,
330 page_height: height,
331 dvi_h: h_off,
332 dvi_v: v_off,
333 cur_s: -1,
334 doing_leaders: false,
335 page: true,
336 sync,
337 };
338 let cur_h = 0;
339 let cur_v = b.height;
340 if self.nodes.id(p) == id::VLIST {
341 self.vlist_out(&mut walker, p, cur_h, cur_v);
342 } else {
343 self.hlist_out(&mut walker, p, cur_h, cur_v);
344 }
345 if let Some(mut be) = walker.backend.take() {
346 self.flush_pending_objects(be.as_mut());
347 self.collect_page_texts();
348 let items = std::mem::take(&mut self.pdf.page);
349 be.close_page_contents(self, &items);
350 self.pdf.page = items;
351 self.resolve_link_actions();
352 let items = std::mem::take(&mut self.pdf.page);
353 be.page_items(self, &items);
354 be.end_page(self);
357 let mut be = self.write_pending_forms(be);
358 be.close_page_resources(self);
359 self.flush_pending_objects(be.as_mut());
360 self.backend = Some(be);
361 let page_images = std::mem::take(&mut self.pdf.page_images);
362 let written = self.backend.as_mut().and_then(|b| b.written_images());
363 for n in written.unwrap_or(page_images) {
364 self.print_image_name(n);
365 }
366 }
367 self.pending_forms.truncate(forms_depth);
368 if let Some(mut d) = walker.dvi.take() {
369 d.end_page();
370 self.dvi = d;
371 }
372 if sync {
373 self.synctex_end_page();
374 }
375 self.total_pages += 1;
376 if let Some(f) = self.progress_hook {
377 f(self);
378 }
379 }
380
381 fn flush_pending_objects(&mut self, be: &mut dyn Backend) {
383 for obj in std::mem::take(&mut self.pdf.pending) {
384 be.write_object(self, &obj);
385 }
386 }
387
388 pub(crate) fn print_map_file_note(&mut self, name: &str) {
391 let path = self
392 .kpse
393 .as_ref()
394 .and_then(|k| k.borrow_mut().find_file(name, retex_kpath::FileFormat::Map));
395 if let Some(path) = path {
396 let mut text = b"{".to_vec();
398 text.extend_from_slice(path.to_string_lossy().as_bytes());
399 text.push(b'}');
400 self.printer.print_lua_bytes(&text);
401 }
402 }
403
404 fn out_glyph(&self, w: &mut Walker, font: u32, chr: u32, h: i32, v: i32, ef: i32) {
407 self.out_glyph_at_depth(w, font, chr, h, v, ef, 0);
408 }
409
410 #[allow(clippy::too_many_arguments)]
412 fn out_glyph_at_depth(
413 &self,
414 w: &mut Walker,
415 font: u32,
416 chr: u32,
417 h: i32,
418 v: i32,
419 ef: i32,
420 depth: u32,
421 ) {
422 if let Some(vf) = &self.font(font).vf
423 && let Some(cmds) = vf.packets.get(&chr)
424 {
425 if depth < 8 {
426 self.out_packet(w, vf, cmds, h, v, ef, depth);
427 }
428 return;
429 }
430 let (x, y) = w.pos(h, v);
431 if let Some(be) = w.backend.as_deref_mut()
433 && self.font(font).chars.contains_key(&chr)
434 {
435 if ef == 0 {
436 be.glyph(self, font, chr, x, y);
437 } else {
438 be.glyph_expanded(self, font, chr, x, y, ef);
439 }
440 }
441 if let Some(d) = w.dvi.as_mut()
442 && self.font(font).chars.contains_key(&chr)
443 {
444 let wd = crate::expansion::expanded_width(self.font_char_dimen(font, chr, 0), ef);
445 d.glyph(self, font, chr, w.dvi_h + h, w.dvi_v + v, wd);
446 }
447 }
448
449 #[allow(clippy::too_many_arguments)]
455 fn out_packet(
456 &self,
457 w: &mut Walker,
458 vf: &crate::fonts::VirtualFont,
459 cmds: &[retex_font::vf::Command],
460 mut h: i32,
461 mut v: i32,
462 ef: i32,
463 depth: u32,
464 ) {
465 use retex_font::vf::Command;
466 let mut f = vf.fonts.first().copied().unwrap_or(0);
467 let mut stack: Vec<(i32, i32)> = Vec::new();
468 for c in cmds {
469 match c {
470 Command::Font(i) => f = vf.fonts.get(*i).copied().unwrap_or(0),
471 Command::Char(c) => {
472 if f != 0 && self.font(f).chars.contains_key(c) {
473 self.out_glyph_at_depth(w, f, *c, h, v, ef, depth + 1);
474 h = h.wrapping_add(crate::expansion::expanded_width(
475 self.font_char_dimen(f, *c, 0),
476 ef,
477 ));
478 }
479 }
480 Command::Rule { height, width } => {
481 if *height > 0 && *width > 0 {
482 Self::out_rule(w, h, v, *width, *height);
483 }
484 h = h.wrapping_add(*width);
485 }
486 Command::Right(x) => h = h.wrapping_add(*x),
487 Command::Down(y) => v = v.wrapping_add(*y),
488 Command::Push => stack.push((h, v)),
489 Command::Pop => {
490 if let Some((sh, sv)) = stack.pop() {
491 (h, v) = (sh, sv);
492 }
493 }
494 Command::Special(_) => {}
495 }
496 }
497 }
498
499 fn out_rule(w: &mut Walker, h: i32, v: i32, width: i32, height: i32) {
502 let (x, y) = w.pos(h, v);
503 if let Some(be) = w.backend.as_deref_mut() {
504 be.rule(x, y, width, height);
505 }
506 if let Some(d) = w.dvi.as_mut() {
507 d.rule(w.dvi_h + h, w.dvi_v + v, width, height);
508 }
509 }
510
511 fn out_box_resource(
515 &mut self,
516 w: &mut Walker,
517 index: i32,
518 h: i32,
519 v: i32,
520 width: i32,
521 height: i32,
522 ) {
523 let n = index as u32;
524 let (x, y) = w.pos(h, v);
525 let Some(mut be) = w.backend.take() else {
526 return;
527 };
528 if self
529 .box_resource_slot(n)
530 .is_some_and(|i| !self.box_resources[i].written)
531 {
532 match self.pending_forms.last_mut() {
533 Some(p) => {
534 if !p.contains(&n) {
535 p.push(n);
536 }
537 }
538 None => be = self.write_box_resource(be, n),
539 }
540 }
541 be.box_form(self, n, x, y, width, height);
542 w.backend = Some(be);
543 }
544
545 pub fn write_box_resource(&mut self, be: Box<dyn Backend>, n: u32) -> Box<dyn Backend> {
549 let Some(slot) = self.box_resource_slot(n) else {
550 return be;
551 };
552 let r = self.box_resources[slot].clone();
553 if r.written {
554 return be;
555 }
556 if self.eqtb.int(ip::TRACINGOUTPUT) > 0 {
560 self.printer.print_nl("");
561 self.printer.print_ln();
562 self.printer.print_str("Completed box being shipped out");
563 }
564 let mut be = be;
565 let (bw, bh, bd) = r.size;
566 self.pending_forms.push(Vec::new());
567 self.form_objects.push(Vec::new());
568 be.begin_box_form(self, n, (bw, bh, bd), &r.attr, &r.resources);
569 let sync = self.synctex_recording();
570 if sync {
571 self.synctex_begin_form(bh + bd);
572 }
573 let mut walker = Walker {
574 backend: Some(be),
575 dvi: None,
576 h_origin: 0,
577 v_origin: 0,
578 page_height: bh + bd,
579 dvi_h: 0,
580 dvi_v: 0,
581 cur_s: -1,
582 doing_leaders: false,
583 page: false,
584 sync,
585 };
586 if self.nodes.id(r.list) == id::VLIST {
587 self.vlist_out(&mut walker, r.list, 0, bh);
588 } else {
589 self.hlist_out(&mut walker, r.list, 0, bh);
590 }
591 let mut be = walker.backend.take().expect("the walker keeps the backend");
592 be.end_box_form(self);
593 self.box_resources[slot].written = true;
594 for obj in self.form_objects.pop().unwrap_or_default() {
595 be.write_object(self, &obj);
596 }
597 be = self.write_pending_forms(be);
598 if sync {
600 self.synctex_end_form();
601 }
602 be.close_box_form_resources(self);
603 for n in be.written_images().unwrap_or_default() {
606 self.print_image_name(n);
607 }
608 be
609 }
610
611 fn write_pending_forms(&mut self, mut be: Box<dyn Backend>) -> Box<dyn Backend> {
614 for n in self.pending_forms.pop().unwrap_or_default() {
615 be = self.write_box_resource(be, n);
616 }
617 be
618 }
619
620 fn out_image(&mut self, w: &mut Walker, index: i32, h: i32, v: i32, width: i32, height: i32) {
622 let (x, y) = w.pos(h, v);
623 if let Some(be) = w.backend.as_deref_mut() {
624 be.image(self, index as u32, x, y, width, height);
625 if self.mark_image_written(index as u32) {
626 self.pdf.page_images.push(index as u32);
627 }
628 }
629 }
630
631 fn out_box(&mut self, w: &mut Walker, p: NodeRef, h: i32, v: i32) {
634 if w.cur_s >= crate::nodes::MAX_NESTING as i32 {
637 return;
638 }
639 if self.nodes.id(p) == id::VLIST {
640 self.vlist_out(w, p, h, v);
641 } else {
642 self.hlist_out(w, p, h, v);
643 }
644 }
645
646 fn enter_box(w: &mut Walker) {
648 w.cur_s += 1;
649 if let Some(d) = w.dvi.as_mut() {
650 if w.cur_s > 0 {
651 d.push(w.cur_s);
652 } else {
653 d.note_depth(w.cur_s);
654 }
655 }
656 }
657
658 fn leave_box(w: &mut Walker) {
660 if w.cur_s > 0
661 && let Some(d) = w.dvi.as_mut()
662 {
663 d.pop();
664 }
665 w.cur_s -= 1;
666 }
667
668 fn hlist_out(&mut self, w: &mut Walker, this_box: NodeRef, mut cur_h: i32, cur_v: i32) {
671 let Some(b) = self.nodes.box_data(this_box).cloned() else {
672 return;
673 };
674 let mut gs = GlueState {
675 cur_g: 0,
676 cur_glue: 0.0,
677 order: b.glue_order,
678 sign: b.glue_sign,
679 set: b.glue_set,
680 };
681 Self::enter_box(w);
682 let base_line = cur_v;
683 let left_edge = cur_h;
684 let ctx = crate::pdfship::BoxCtx {
685 left: left_edge,
686 base: base_line,
687 width: b.width,
688 height: b.height,
689 depth: b.depth,
690 };
691 if !w.doing_leaders {
692 self.ship_continue_links(w, &ctx);
693 }
694 if w.sync {
695 let f = self.sync_of(this_box);
696 self.synctex
697 .box_open(true, f, cur_h, cur_v, [b.width, b.height, b.depth]);
698 }
699 let mut p = b.head;
700 while !p.is_null() {
701 let next = self.nodes.next(p);
702 if w.sync {
703 self.sync_hlist_node(p, cur_h, base_line);
704 }
705 let n = self.nodes.get(p);
706 let n_id = n.id;
707 match &n.data {
708 NodeData::Glyph(g) => {
709 let (font, chr, xo, yo, ef) =
710 (g.font, g.chr, g.xoffset, g.yoffset, g.expansion_factor);
711 if !self.pdf.map_note_done
712 && w.backend.is_some()
713 && self.fonts.char_metrics(font, chr).is_some()
714 {
715 self.pdf.map_note_done = true;
719 self.print_map_file_note("pdftex.map");
720 }
721 if self.fonts.char_metrics(font, chr).is_none() {
722 self.shipout_lost_char(font, chr);
723 }
724 self.out_glyph(w, font, chr, cur_h + xo, base_line - yo, ef);
725 cur_h +=
726 crate::expansion::expanded_width(self.font_char_dimen(font, chr, 0), ef);
727 }
728 NodeData::Box(inner) => {
729 let (iw, shift, head) = (inner.width, inner.shift, inner.head);
730 if !head.is_null() {
731 self.out_box(w, p, cur_h, base_line + shift);
732 } else if w.sync {
733 let size = [iw, inner.height, inner.depth];
734 let f = self.sync_of(p);
735 self.synctex
736 .void_box(n_id == id::HLIST, f, cur_h, base_line, size);
737 }
738 cur_h += iw;
739 }
740 NodeData::Rule {
741 width,
742 depth,
743 height,
744 index,
745 ..
746 } => {
747 let (wd, ht, dpt, idx) = (*width, *height, *depth, *index);
748 if n.subtype == crate::image::IMAGE_RULE {
749 self.out_image(w, idx, cur_h, base_line + dpt, wd, ht + dpt);
750 } else if n.subtype == crate::image::BOX_RESOURCE_RULE {
751 self.out_box_resource(w, idx, cur_h, base_line + dpt, wd, ht + dpt);
752 } else {
753 Self::hlist_rule(w, cur_h, base_line, wd, ht, dpt, &b);
754 }
755 cur_h += wd;
756 if w.sync {
757 let f = self.sync_of(p);
758 self.synctex.rule(f, cur_h, base_line, [wd, ht, dpt]);
759 }
760 }
761 NodeData::Glue { spec, leader } => {
762 let (spec, leader, sub) = (*spec, *leader, n.subtype);
763 let wd = gs.advance(&spec);
764 let box_leaders = sub >= glue_sub::A_LEADERS
765 && !leader.is_null()
766 && self.nodes.id(leader) != id::RULE;
767 if sub >= glue_sub::A_LEADERS && !leader.is_null() {
768 self.hlist_leaders(w, leader, sub, cur_h, left_edge, base_line, wd, &b);
769 }
770 cur_h += wd;
771 if w.sync && !box_leaders {
772 let f = self.sync_of(p);
773 self.synctex.glue(f, cur_h, base_line);
774 }
775 }
776 NodeData::Kern {
777 kern,
778 expansion_factor,
779 } => {
780 let wd = kern + expansion_factor;
781 if w.sync {
782 let f = self.sync_of(p);
785 self.synctex.kern(f, *kern);
786 }
787 cur_h += wd;
788 }
789 NodeData::Math { surround, glue } => {
790 let g = *glue;
791 if w.sync {
792 let f = self.sync_of(p);
793 self.synctex.math(f, cur_h, base_line);
794 }
795 cur_h += surround;
796 if g != Glue::ZERO {
797 cur_h += gs.advance(&g);
798 }
799 }
800 NodeData::MarginKern { width, .. } => cur_h += width,
801 NodeData::Whatsit(_) => self.out_what(w, p, cur_h, base_line, &ctx),
802 NodeData::Disc { replace, .. } => {
803 let r = *replace;
806 let mut q = r;
807 while !q.is_null() {
808 if let NodeData::Glyph(g) = &self.nodes.get(q).data {
809 let (font, chr, ef) = (g.font, g.chr, g.expansion_factor);
810 self.out_glyph(w, font, chr, cur_h, base_line, ef);
811 if w.sync {
812 self.synctex.glyph();
813 }
814 cur_h += crate::expansion::expanded_width(
815 self.font_char_dimen(font, chr, 0),
816 ef,
817 );
818 } else if let NodeData::Kern {
819 kern,
820 expansion_factor,
821 } = &self.nodes.get(q).data
822 {
823 let wd = kern + expansion_factor;
824 if w.sync {
825 self.synctex.end_run(cur_h, base_line);
826 let f = self.sync_of(q);
827 self.synctex.kern(f, *kern);
828 }
829 cur_h += wd;
830 }
831 q = self.nodes.next(q);
832 }
833 }
834 _ => {}
835 }
836 p = next;
837 }
838 if !w.doing_leaders {
839 self.ship_end_box_links(w, &ctx);
840 }
841 if w.sync {
842 self.synctex.end_run(cur_h, base_line);
843 let f = self.sync_of(this_box);
844 self.synctex.box_close(true, f, cur_h, base_line);
845 }
846 Self::leave_box(w);
847 }
848
849 #[inline]
852 fn sync_hlist_node(&mut self, p: NodeRef, cur_h: i32, base_line: i32) {
853 match self.nodes.id(p) {
854 id::GLYPH => self.synctex.glyph(),
855 id::MARK | id::INS | id::ADJUST | id::BOUNDARY | id::LOCAL_PAR => {}
856 _ => self.synctex.end_run(cur_h, base_line),
857 }
858 }
859
860 fn hlist_rule(
863 w: &mut Walker,
864 cur_h: i32,
865 base_line: i32,
866 wd: i32,
867 ht: i32,
868 dpt: i32,
869 this_box: &crate::nodes::BoxData,
870 ) {
871 let ht = if ht == RUNNING { this_box.height } else { ht };
872 let dpt = if dpt == RUNNING { this_box.depth } else { dpt };
873 let total = ht + dpt;
874 if total > 0 && wd > 0 {
875 Self::out_rule(w, cur_h, base_line + dpt, wd, total);
876 }
877 }
878
879 fn vlist_out(&mut self, w: &mut Walker, this_box: NodeRef, cur_h: i32, cur_v: i32) {
881 let Some(b) = self.nodes.box_data(this_box).cloned() else {
882 return;
883 };
884 let mut gs = GlueState {
885 cur_g: 0,
886 cur_glue: 0.0,
887 order: b.glue_order,
888 sign: b.glue_sign,
889 set: b.glue_set,
890 };
891 Self::enter_box(w);
892 let left_edge = cur_h;
893 let ctx = crate::pdfship::BoxCtx {
894 left: left_edge,
895 base: cur_v,
896 width: b.width,
897 height: b.height,
898 depth: b.depth,
899 };
900 let mut cur_v = cur_v - b.height;
901 let top_edge = cur_v;
902 if w.sync {
903 let f = self.sync_of(this_box);
904 self.synctex
905 .box_open(false, f, cur_h, cur_v, [b.width, b.height, b.depth]);
906 }
907 let mut p = b.head;
908 while !p.is_null() {
909 let next = self.nodes.next(p);
910 let n = self.nodes.get(p);
911 match &n.data {
912 NodeData::Box(inner) => {
913 let (ih, idp, shift, head) =
914 (inner.height, inner.depth, inner.shift, inner.head);
915 if head.is_null() {
916 cur_v += ih + idp;
917 if w.sync {
918 let size = [inner.width, ih, idp];
919 let hlist = n.id == id::HLIST;
920 let f = self.sync_of(p);
921 self.synctex.void_box(hlist, f, left_edge, cur_v, size);
922 }
923 } else {
924 cur_v += ih;
925 self.out_box(w, p, left_edge + shift, cur_v);
926 cur_v += idp;
927 }
928 }
929 NodeData::Rule {
930 width,
931 depth,
932 height,
933 index,
934 ..
935 } => {
936 let wd = if *width == RUNNING { b.width } else { *width };
937 let total = height + depth;
938 let idx = *index;
939 cur_v += total;
940 if n.subtype == crate::image::IMAGE_RULE {
941 self.out_image(w, idx, left_edge, cur_v, wd, total);
942 } else if n.subtype == crate::image::BOX_RESOURCE_RULE {
943 self.out_box_resource(w, idx, left_edge, cur_v, wd, total);
944 } else if total > 0 && wd > 0 {
945 Self::out_rule(w, left_edge, cur_v, wd, total);
946 }
947 }
948 NodeData::Glue { spec, leader } => {
949 let (spec, leader, sub) = (*spec, *leader, n.subtype);
950 let ht = gs.advance(&spec);
951 if sub >= glue_sub::A_LEADERS && !leader.is_null() {
952 cur_v =
953 self.vlist_leaders(w, leader, sub, cur_v, left_edge, top_edge, ht, &b);
954 } else {
955 cur_v += ht;
956 }
957 }
958 NodeData::Kern { kern, .. } => cur_v += kern,
959 NodeData::Whatsit(_) => self.out_what(w, p, left_edge, cur_v, &ctx),
960 _ => {}
961 }
962 p = next;
963 }
964 if w.sync {
965 let f = self.sync_of(this_box);
966 self.synctex.box_close(false, f, left_edge, cur_v);
967 }
968 Self::leave_box(w);
969 }
970
971 fn leader_start(sub: u16, cur: i32, edge0: i32, size: i32, step: i32) -> (i32, i32) {
975 if sub == glue_sub::C_LEADERS || sub == glue_sub::X_LEADERS {
976 let lq = size / step;
977 let lr = size % step;
978 if sub == glue_sub::C_LEADERS {
979 (cur + lr / 2, 0)
980 } else {
981 let lx = lr / (lq + 1);
982 (cur + (lr - (lq - 1) * lx) / 2, lx)
983 }
984 } else {
985 let mut c = edge0 + step * ((cur - edge0) / step);
986 if c < cur {
987 c += step;
988 }
989 (c, 0)
990 }
991 }
992
993 #[allow(clippy::too_many_arguments)]
995 fn hlist_leaders(
996 &mut self,
997 w: &mut Walker,
998 leader: NodeRef,
999 sub: u16,
1000 cur_h: i32,
1001 left_edge: i32,
1002 base_line: i32,
1003 wd: i32,
1004 this_box: &crate::nodes::BoxData,
1005 ) {
1006 let lb = match &self.nodes.get(leader).data {
1007 NodeData::Rule { height, depth, .. } => {
1008 let (ht, dpt) = (*height, *depth);
1009 Self::hlist_rule(w, cur_h, base_line, wd, ht, dpt, this_box);
1010 return;
1011 }
1012 NodeData::Box(lb) => lb.clone(),
1013 _ => return,
1014 };
1015 let leader_wd = lb.width;
1016 if leader_wd <= 0 || wd <= 0 {
1017 return;
1018 }
1019 let rule_wd = wd + 10; let edge = cur_h + rule_wd;
1021 let (mut h, lx) = Self::leader_start(sub, cur_h, left_edge, rule_wd, leader_wd);
1022 while h + leader_wd <= edge {
1025 let outer = w.doing_leaders;
1026 w.doing_leaders = true;
1027 self.out_box(w, leader, h, base_line + lb.shift);
1028 w.doing_leaders = outer;
1029 h += leader_wd + lx;
1030 }
1031 }
1032
1033 #[allow(clippy::too_many_arguments)]
1036 fn vlist_leaders(
1037 &mut self,
1038 w: &mut Walker,
1039 leader: NodeRef,
1040 sub: u16,
1041 cur_v: i32,
1042 left_edge: i32,
1043 top_edge: i32,
1044 ht: i32,
1045 this_box: &crate::nodes::BoxData,
1046 ) -> i32 {
1047 let lb = match &self.nodes.get(leader).data {
1048 NodeData::Rule { width, .. } => {
1049 let wd = if *width == RUNNING {
1050 this_box.width
1051 } else {
1052 *width
1053 };
1054 if ht > 0 && wd > 0 {
1055 Self::out_rule(w, left_edge, cur_v + ht, wd, ht);
1056 }
1057 return cur_v + ht;
1058 }
1059 NodeData::Box(lb) => lb.clone(),
1060 _ => return cur_v + ht,
1061 };
1062 let leader_ht = lb.height + lb.depth;
1063 if leader_ht <= 0 || ht <= 0 {
1064 return cur_v + ht;
1065 }
1066 let rule_ht = ht + 10; let edge = cur_v + rule_ht;
1068 let (mut v, lx) = Self::leader_start(sub, cur_v, top_edge, rule_ht, leader_ht);
1069 while v + leader_ht <= edge {
1070 let outer = w.doing_leaders;
1071 w.doing_leaders = true;
1072 self.out_box(w, leader, left_edge + lb.shift, v + lb.height);
1073 w.doing_leaders = outer;
1074 v += leader_ht + lx;
1075 }
1076 edge - 10
1077 }
1078
1079 fn out_what(
1081 &mut self,
1082 w: &mut Walker,
1083 p: NodeRef,
1084 h: i32,
1085 v: i32,
1086 ctx: &crate::pdfship::BoxCtx,
1087 ) {
1088 let NodeData::Whatsit(wh) = &self.nodes.get(p).data else {
1089 return;
1090 };
1091 let (x, y) = w.pos(h, v);
1092 match (**wh).clone() {
1093 Whatsit::Open { stream, name } if !w.doing_leaders => {
1094 self.open_write_stream(stream as i32, &name)
1095 }
1096 Whatsit::Write { stream, data } if !w.doing_leaders => self.write_out(stream, &data),
1097 Whatsit::Close { stream } if !w.doing_leaders => self.close_write_stream(stream as i32),
1098 Whatsit::Special { data } => {
1099 if let Some(d) = w.dvi.as_mut() {
1100 d.special(w.dvi_h + h, w.dvi_v + v, &data);
1101 } else if let Some(be) = w.backend.as_deref_mut() {
1102 if data.len() >= 4 && data[..4].eq_ignore_ascii_case(b"pdf:") {
1106 let rest = &data[4..];
1107 let (mode, text) = [
1108 (&b"direct:"[..], 2),
1109 (b"page:", 1),
1110 (b"raw:", 3),
1111 (b"text:", 4),
1112 (b"origin:", 0),
1113 ]
1114 .iter()
1115 .find_map(|(k, m)| rest.strip_prefix(*k).map(|t| (*m, t)))
1116 .unwrap_or((crate::pdfext::literal_mode::ORIGIN, rest));
1117 be.literal(mode, text, x, y);
1118 } else {
1119 self.printer.print_nl("Non-PDF special ignored!");
1120 }
1121 }
1122 }
1123 Whatsit::PdfLiteral { mode, data, .. } => {
1124 let bytes = match data {
1125 crate::nodes::LateData::Bytes(b)
1126 | crate::nodes::LateData::Lua { data: b, .. } => b,
1127 crate::nodes::LateData::Tokens(t) => {
1128 let toks = self.expand_late_tokens(&t);
1129 self.print_to_string(|e| e.show_token_list(&toks, 0, toks.len(), i64::MAX))
1130 }
1131 };
1132 if let Some(be) = w.backend.as_deref_mut() {
1133 be.literal(mode, &bytes, x, y);
1134 }
1135 }
1136 Whatsit::LateLua { data, .. } => {
1137 let code = match data {
1138 crate::nodes::LateData::Bytes(b)
1139 | crate::nodes::LateData::Lua { data: b, .. } => b,
1140 crate::nodes::LateData::Tokens(t) => {
1141 let toks = self.expand_late_tokens(&t);
1142 self.lua_code(&toks)
1143 }
1144 };
1145 self.pdf.pos = (x, y);
1148 let outer = self.pdf.late.replace(Default::default());
1149 self.run_lua(&code);
1150 let ctx = std::mem::replace(&mut self.pdf.late, outer).unwrap_or_default();
1151 if let Some(be) = w.backend.as_deref_mut() {
1152 for (mode, text) in &ctx.prints {
1153 be.literal(*mode, text, x, y);
1154 }
1155 }
1156 self.pdf
1157 .page
1158 .annots
1159 .extend(ctx.annots.into_iter().map(|a| (a, None)));
1160 }
1161 Whatsit::PdfRefobj { objnum } if !w.doing_leaders => self.ship_refobj(w, objnum),
1162 Whatsit::PdfAnnot { dims, objnum, data } if !w.doing_leaders => {
1163 self.ship_annot(w, ctx, h, v, dims, objnum, &data)
1164 }
1165 Whatsit::PdfStartLink {
1166 dims,
1167 objnum,
1168 attr,
1169 action,
1170 } if !w.doing_leaders => {
1171 self.ship_start_link(w, ctx, h, v, dims, objnum, &attr, &action)
1172 }
1173 Whatsit::PdfEndLink if !w.doing_leaders => self.ship_end_link(w, h, v),
1174 Whatsit::PdfDest {
1175 id,
1176 view,
1177 structure,
1178 } if !w.doing_leaders => self.ship_dest(w, ctx, h, v, &id, &view, structure),
1179 Whatsit::SavePos => {
1180 self.last_x_pos = x;
1181 self.last_y_pos = y;
1182 }
1183 Whatsit::PdfSave => {
1184 if let Some(be) = w.backend.as_deref_mut() {
1185 be.save(x, y);
1186 }
1187 }
1188 Whatsit::PdfRestore => {
1189 if let Some(be) = w.backend.as_deref_mut() {
1190 be.restore(x, y);
1191 }
1192 }
1193 Whatsit::PdfSetmatrix { data } => {
1194 if let Some(be) = w.backend.as_deref_mut() {
1195 be.set_matrix(x, y, &data);
1196 }
1197 }
1198 Whatsit::PdfColorstack { stack, cmd, data } => {
1199 let out = self.colorstack_op(stack, cmd, data);
1200 if let (Some((mode, lit)), Some(be)) = (out, w.backend.as_deref_mut()) {
1201 be.literal(mode, &lit, x, y);
1202 }
1203 }
1204 _ => {}
1205 }
1206 }
1207
1208 fn colorstack_op(&mut self, stack: i32, cmd: u8, data: Vec<u8>) -> Option<(u8, Vec<u8>)> {
1210 use crate::pdfext::colorstack_cmd as c;
1211 self.pdf_ensure_colorstacks();
1212 let def = self.pdf.colorstacks.get(stack.max(0) as usize).cloned()?;
1213 let st = self
1214 .colorstack_values
1215 .entry(stack)
1216 .or_insert_with(|| vec![def.init.clone()]);
1217 match cmd {
1218 c::SET => {
1219 if let Some(top) = st.last_mut() {
1220 *top = data;
1221 } else {
1222 st.push(data);
1223 }
1224 }
1225 c::PUSH => st.push(data),
1226 c::POP => {
1227 if st.len() > 1 {
1228 st.pop();
1229 }
1230 }
1231 _ => {}
1232 }
1233 let lit = st.last().cloned().unwrap_or_default();
1234 Some((def.mode, lit))
1235 }
1236
1237 fn expand_late_tokens(&mut self, list: &crate::token::TokenList) -> Vec<crate::token::Token> {
1240 use crate::token::Token;
1241 use std::rc::Rc;
1242 self.ins_list(Rc::from(vec![
1243 Token::RIGHT_BRACE,
1244 Token::cs(self.cs.end_write),
1245 ]));
1246 self.begin_token_list(list.clone(), crate::input::TokKind::WriteText);
1247 self.ins_list(Rc::from(vec![Token::LEFT_BRACE]));
1248 let toks = self.scan_toks(false, true);
1249 self.get_token();
1250 toks
1251 }
1252
1253 pub fn finish_backend(&mut self) {
1255 if self.dvi.pages() > 0 {
1256 self.finish_dvi();
1257 } else if self.pdf.output_opened && self.eqtb.int(ip::OUTPUTMODE) <= 0 {
1258 let _ = std::fs::write(self.output_path("dvi"), b"");
1260 }
1261 let Some(mut be) = self.backend.take() else {
1262 return;
1263 };
1264 if self.pdf.fatal || self.total_pages == 0 {
1265 be.abandon(self.pdf.fatal);
1266 self.backend = Some(be);
1267 return;
1268 }
1269 if self.eqtb.int(ip::DRAFTMODE) > 0 && self.eqtb.int(ip::OUTPUTMODE) > 0 {
1270 be.abandon(true);
1275 self.backend = Some(be);
1276 self.printer
1277 .print_nl("warning (pdf backend): draftmode enabled, not changing output pdf");
1278 self.printer.print_ln();
1279 return;
1280 }
1281 if self.total_pages > 0 {
1282 self.pdf_finish_document();
1283 self.flush_pending_objects(be.as_mut());
1284 }
1285 if self.total_pages > 0
1286 && let Some(fin) = be.finish(self)
1287 {
1288 for f in &fin.embedded {
1291 let mut text = b"<".to_vec();
1292 text.extend_from_slice(f.as_bytes());
1293 text.push(b'>');
1294 self.printer.print_lua_bytes(&text);
1295 }
1296 self.pdf.objstm_stats = fin.objstm;
1297 self.print_output_written(&fin.file, self.total_pages, fin.size);
1298 }
1299 self.backend = Some(be);
1300 }
1301
1302 pub(crate) fn output_path(&self, ext: &str) -> std::path::PathBuf {
1304 let job =
1305 String::from_utf8_lossy(self.job_name.as_deref().unwrap_or(b"texput")).into_owned();
1306 let name = format!("{job}.{ext}");
1307 match &self.files.output_dir {
1308 Some(d) => d.join(&name),
1309 None => std::path::PathBuf::from(&name),
1310 }
1311 }
1312
1313 fn finish_dvi(&mut self) {
1315 let mut dvi = std::mem::take(&mut self.dvi);
1316 let pages = dvi.pages();
1317 let data = dvi.finish(self);
1318 let path = self.output_path("dvi");
1319 let name = path
1320 .file_name()
1321 .map(|n| n.to_string_lossy().into_owned())
1322 .unwrap_or_default();
1323 if std::fs::write(&path, &data).is_err() {
1324 self.printer.print_nl("! I can't write on file `");
1325 self.printer.print_bytes(name.as_bytes());
1326 self.printer.print_str("'.");
1327 return;
1328 }
1329 self.print_output_written(&name, pages, data.len() as u64);
1330 }
1331
1332 fn print_output_written(&mut self, name: &str, pages: u32, size: u64) {
1334 self.output_written = Some((name.to_owned(), pages, size));
1335 self.printer.print_nl("Output written on ");
1336 self.printer.print_bytes(name.as_bytes());
1337 self.printer.print_str(" (");
1338 self.printer.print_int(i64::from(pages));
1339 self.printer
1340 .print_str(if pages == 1 { " page" } else { " pages" });
1341 self.printer.print_str(", ");
1342 self.printer.print_int(size as i64);
1343 self.printer.print_str(" bytes).");
1344 }
1345}
1346
1347pub(crate) struct Walker {
1350 backend: Option<Box<dyn Backend>>,
1352 dvi: Option<crate::dvi::DviWriter>,
1354 h_origin: i32,
1355 v_origin: i32,
1356 page_height: i32,
1357 dvi_h: i32,
1359 dvi_v: i32,
1360 pub(crate) cur_s: i32,
1362 doing_leaders: bool,
1364 pub(crate) page: bool,
1367 sync: bool,
1369}
1370
1371impl Walker {
1372 pub(crate) fn pos(&self, h: i32, v: i32) -> (i32, i32) {
1375 (self.h_origin + h, self.page_height - (self.v_origin + v))
1376 }
1377}