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