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
166fn items_before_width(e: &Engine, head: NodeRef, q: NodeRef) -> i32 {
168 let mut w = 0;
169 let mut r = head;
170 while !r.is_null() && r != q {
171 w += e.replace_item_width(r);
172 r = e.nodes.next(r);
173 }
174 w
175}
176
177#[derive(Clone)]
179struct GlueState {
180 cur_g: i32,
181 cur_glue: f64,
182 order: u8,
183 sign: u8,
184 set: f64,
185}
186
187impl GlueState {
188 fn advance(&mut self, g: &Glue) -> i32 {
190 let mut w = g.width - self.cur_g;
191 if self.sign != 0 {
192 let applies = if self.sign == 1 {
193 g.stretch_order == self.order
194 } else {
195 g.shrink_order == self.order
196 };
197 if applies {
198 if self.sign == 1 {
199 self.cur_glue += f64::from(g.stretch);
200 } else {
201 self.cur_glue -= f64::from(g.shrink);
202 }
203 let t = (self.set * self.cur_glue).clamp(-1_000_000_000.0, 1_000_000_000.0);
204 self.cur_g = t.round() as i32;
205 }
206 }
207 w += self.cur_g;
208 w
209 }
210}
211
212impl Engine {
213 fn page_number_callback(&mut self, name: &str) -> bool {
216 let Some(lua) = self.lua.clone() else {
217 return false;
218 };
219 if !lua.has_callback(name) {
220 return false;
221 }
222 if let Err(e) = lua.call_simple(self, name) {
223 self.lua_error(&e);
224 }
225 true
226 }
227
228 pub fn ship_out(&mut self, p: NodeRef) {
230 self.pdf.page_annots.clear();
231 let tracing = self.eqtb.int(ip::TRACINGOUTPUT) > 0;
232 if tracing {
233 self.printer.print_nl("");
234 self.printer.print_ln();
235 self.printer.print_str("Completed box being shipped out");
236 }
237 if !self.page_number_callback("start_page_number") {
240 if self.printer.term_offset > self.printer.max_print_line.saturating_sub(9) {
241 self.printer.print_ln();
242 } else if self.printer.term_offset > 0 || self.printer.file_offset > 0 {
243 self.printer.print_char(' ' as u32);
244 }
245 self.printer.print_char('[' as u32);
246 let mut j = 9;
247 while j > 0 && self.eqtb.count[j].val == 0 {
248 j -= 1;
249 }
250 for k in 0..=j {
251 self.printer.print_int(self.eqtb.count[k].val.into());
252 if k < j {
253 self.printer.print_char('.' as u32);
254 }
255 }
256 }
257 self.printer.update_terminal();
258 if p.is_null() {
259 self.luatex_warning(
261 "backend",
262 "pagedir differs from bodydir, the output may be placed wrongly on the page",
263 );
264 let msg = "no vlist or hlist in (xform) shipout";
265 if self.eqtb.int(ip::OUTPUTMODE) > 0 {
266 self.pdf_fatal(msg);
267 }
268 self.luatex_fatal("pdf backend", msg);
269 }
270 if tracing {
271 if !self.page_number_callback("stop_page_number") {
272 self.printer.print_char(']' as u32);
273 }
274 self.begin_diagnostic();
275 self.show_box(p);
276 self.end_diagnostic(true);
277 }
278 self.ship_box(p);
279 if !tracing && !self.page_number_callback("stop_page_number") {
280 self.printer.print_char(']' as u32);
281 }
282 self.dead_cycles = 0;
283 self.printer.update_terminal();
284 self.nodes.flush_list(p);
285 }
286
287 fn ship_box(&mut self, p: NodeRef) {
289 let Some(b) = self.nodes.box_data(p).cloned() else {
290 return;
291 };
292 let pdf = self.eqtb.int(ip::OUTPUTMODE) > 0;
293 let h_origin = self.eqtb.dimen(crate::backend::H_ORIGIN);
295 let v_origin = self.eqtb.dimen(crate::backend::V_ORIGIN);
296 let h_off = self.eqtb.dimen(dp::HOFFSET);
297 let v_off = self.eqtb.dimen(dp::VOFFSET);
298 let mut width = self.eqtb.dimen(dp::PAGEWIDTH);
299 let mut height = self.eqtb.dimen(dp::PAGEHEIGHT);
300 const ONE_INCH: i32 = 4_736_286;
303 if width <= 0 {
304 width = b.width + 2 * (ONE_INCH + h_off);
305 }
306 if height <= 0 {
307 height = b.height + b.depth + 2 * (ONE_INCH + v_off);
308 }
309 let mut backend = if pdf { self.backend.take() } else { None };
310 let forms_depth = self.pending_forms.len();
311 self.pending_forms.push(Vec::new());
312 if let Some(be) = backend.as_mut() {
313 self.pdf.page = Default::default();
314 be.begin_page(self, width, height);
315 if let Some(n) = be.current_page_object() {
316 let page = self.total_pages as i32 + 1;
317 self.pdf.page_refs.insert(page, n);
318 self.pdf.kinds.insert(n, crate::pdfext::ObjKind::Page);
319 }
320 let (x, y) = (h_origin + h_off, height - (v_origin + v_off + b.height));
322 for (mode, lit) in self.page_colorstack_literals() {
323 be.literal(mode, &lit, x, y);
324 }
325 }
326 let dvi = if pdf {
327 None
328 } else {
329 let mut d = std::mem::take(&mut self.dvi);
330 d.begin_page(self, (b.width, b.height, b.depth), (h_off, v_off));
331 Some(d)
332 };
333 let sync = self.synctex_recording();
334 if sync {
335 self.synctex_begin_page((h_origin + h_off, v_origin + v_off));
336 }
337 let mut walker = Walker {
338 backend,
339 dvi,
340 h_origin: h_origin + h_off,
341 v_origin: v_origin + v_off,
342 page_height: height,
343 dvi_h: h_off,
344 dvi_v: v_off,
345 cur_s: -1,
346 doing_leaders: false,
347 page: true,
348 sync,
349 };
350 let cur_h = if self.nodes.id(p) == id::HLIST && b.dir == 1 {
353 -b.width
354 } else {
355 0
356 };
357 let cur_v = b.height;
358 if self.nodes.id(p) == id::VLIST {
359 self.vlist_out(&mut walker, p, cur_h, cur_v);
360 } else {
361 self.hlist_out(&mut walker, p, cur_h, cur_v);
362 }
363 if let Some(mut be) = walker.backend.take() {
364 self.flush_pending_objects(be.as_mut());
365 self.collect_page_texts();
366 let items = std::mem::take(&mut self.pdf.page);
367 be.close_page_contents(self, &items);
368 self.pdf.page = items;
369 self.resolve_link_actions();
370 let items = std::mem::take(&mut self.pdf.page);
371 be.page_items(self, &items);
372 be.end_page(self);
375 let mut be = self.write_pending_forms(be);
376 be.close_page_resources(self);
377 self.flush_pending_objects(be.as_mut());
378 self.backend = Some(be);
379 let page_images = std::mem::take(&mut self.pdf.page_images);
380 let written = self.backend.as_mut().and_then(|b| b.written_images());
381 for n in written.unwrap_or(page_images) {
382 self.print_image_name(n);
383 }
384 }
385 self.pending_forms.truncate(forms_depth);
386 if let Some(mut d) = walker.dvi.take() {
387 d.end_page();
388 self.dvi = d;
389 }
390 if sync {
391 self.synctex_end_page();
392 }
393 self.total_pages += 1;
394 if let Some(f) = self.progress_hook {
395 f(self);
396 }
397 }
398
399 fn flush_pending_objects(&mut self, be: &mut dyn Backend) {
401 for obj in std::mem::take(&mut self.pdf.pending) {
402 be.write_object(self, &obj);
403 }
404 }
405
406 pub(crate) fn print_map_file_note(&mut self, name: &str) {
409 let path = self
410 .kpse
411 .as_ref()
412 .and_then(|k| k.borrow_mut().find_file(name, retex_kpath::FileFormat::Map));
413 if let Some(path) = path {
414 let mut text = b"{".to_vec();
416 text.extend_from_slice(path.to_string_lossy().as_bytes());
417 text.push(b'}');
418 self.printer.print_lua_bytes(&text);
419 }
420 }
421
422 fn out_glyph(&self, w: &mut Walker, font: u32, chr: u32, h: i32, v: i32, ef: i32) {
425 self.out_glyph_at_depth(w, font, chr, h, v, ef, 0);
426 }
427
428 #[allow(clippy::too_many_arguments)]
430 fn out_glyph_at_depth(
431 &self,
432 w: &mut Walker,
433 font: u32,
434 chr: u32,
435 h: i32,
436 v: i32,
437 ef: i32,
438 depth: u32,
439 ) {
440 if let Some(vf) = &self.font(font).vf
441 && let Some(cmds) = vf.packets.get(&chr)
442 {
443 if depth < 8 {
444 self.out_packet(w, vf, cmds, h, v, ef, depth);
445 }
446 return;
447 }
448 let (x, y) = w.pos(h, v);
449 if let Some(be) = w.backend.as_deref_mut()
451 && self.font(font).chars.contains_key(&chr)
452 {
453 if ef == 0 {
454 be.glyph(self, font, chr, x, y);
455 } else {
456 be.glyph_expanded(self, font, chr, x, y, ef);
457 }
458 }
459 if let Some(d) = w.dvi.as_mut()
460 && self.font(font).chars.contains_key(&chr)
461 {
462 let wd = crate::expansion::expanded_width(self.font_char_dimen(font, chr, 0), ef);
463 d.glyph(self, font, chr, w.dvi_h + h, w.dvi_v + v, wd);
464 }
465 }
466
467 #[allow(clippy::too_many_arguments)]
473 fn out_packet(
474 &self,
475 w: &mut Walker,
476 vf: &crate::fonts::VirtualFont,
477 cmds: &[retex_font::vf::Command],
478 mut h: i32,
479 mut v: i32,
480 ef: i32,
481 depth: u32,
482 ) {
483 use retex_font::vf::Command;
484 let mut f = vf.fonts.first().copied().unwrap_or(0);
485 let mut stack: Vec<(i32, i32)> = Vec::new();
486 for c in cmds {
487 match c {
488 Command::Font(i) => f = vf.fonts.get(*i).copied().unwrap_or(0),
489 Command::Char(c) => {
490 if f != 0 && self.font(f).chars.contains_key(c) {
491 self.out_glyph_at_depth(w, f, *c, h, v, ef, depth + 1);
492 h = h.wrapping_add(crate::expansion::expanded_width(
493 self.font_char_dimen(f, *c, 0),
494 ef,
495 ));
496 }
497 }
498 Command::Rule { height, width } => {
499 if *height > 0 && *width > 0 {
500 Self::out_rule(w, h, v, *width, *height);
501 }
502 h = h.wrapping_add(*width);
503 }
504 Command::Right(x) => h = h.wrapping_add(*x),
505 Command::Down(y) => v = v.wrapping_add(*y),
506 Command::Push => stack.push((h, v)),
507 Command::Pop => {
508 if let Some((sh, sv)) = stack.pop() {
509 (h, v) = (sh, sv);
510 }
511 }
512 Command::Special(_) => {}
513 }
514 }
515 }
516
517 fn out_rule(w: &mut Walker, h: i32, v: i32, width: i32, height: i32) {
520 let (x, y) = w.pos(h, v);
521 if let Some(be) = w.backend.as_deref_mut() {
522 be.rule(x, y, width, height);
523 }
524 if let Some(d) = w.dvi.as_mut() {
525 d.rule(w.dvi_h + h, w.dvi_v + v, width, height);
526 }
527 }
528
529 fn out_box_resource(
533 &mut self,
534 w: &mut Walker,
535 index: i32,
536 h: i32,
537 v: i32,
538 width: i32,
539 height: i32,
540 ) {
541 let n = index as u32;
542 let (x, y) = w.pos(h, v);
543 let Some(mut be) = w.backend.take() else {
544 return;
545 };
546 if self
547 .box_resource_slot(n)
548 .is_some_and(|i| !self.box_resources[i].written)
549 {
550 match self.pending_forms.last_mut() {
551 Some(p) => {
552 if !p.contains(&n) {
553 p.push(n);
554 }
555 }
556 None => be = self.write_box_resource(be, n),
557 }
558 }
559 be.box_form(self, n, x, y, width, height);
560 w.backend = Some(be);
561 }
562
563 pub fn write_box_resource(&mut self, be: Box<dyn Backend>, n: u32) -> Box<dyn Backend> {
567 let Some(slot) = self.box_resource_slot(n) else {
568 return be;
569 };
570 let r = self.box_resources[slot].clone();
571 if r.written {
572 return be;
573 }
574 if self.eqtb.int(ip::TRACINGOUTPUT) > 0 {
578 self.printer.print_nl("");
579 self.printer.print_ln();
580 self.printer.print_str("Completed box being shipped out");
581 }
582 let mut be = be;
583 let (bw, bh, bd) = r.size;
584 self.pending_forms.push(Vec::new());
585 self.form_objects.push(Vec::new());
586 be.begin_box_form(self, n, (bw, bh, bd), &r.attr, &r.resources);
587 let sync = self.synctex_recording();
588 if sync {
589 self.synctex_begin_form(bh + bd);
590 }
591 let mut walker = Walker {
592 backend: Some(be),
593 dvi: None,
594 h_origin: 0,
595 v_origin: 0,
596 page_height: bh + bd,
597 dvi_h: 0,
598 dvi_v: 0,
599 cur_s: -1,
600 doing_leaders: false,
601 page: false,
602 sync,
603 };
604 if self.nodes.id(r.list) == id::VLIST {
605 self.vlist_out(&mut walker, r.list, 0, bh);
606 } else {
607 self.hlist_out(&mut walker, r.list, 0, bh);
608 }
609 let mut be = walker.backend.take().expect("the walker keeps the backend");
610 be.end_box_form(self);
611 self.box_resources[slot].written = true;
612 for obj in self.form_objects.pop().unwrap_or_default() {
613 be.write_object(self, &obj);
614 }
615 be = self.write_pending_forms(be);
616 if sync {
618 self.synctex_end_form();
619 }
620 be.close_box_form_resources(self);
621 for n in be.written_images().unwrap_or_default() {
624 self.print_image_name(n);
625 }
626 be
627 }
628
629 fn write_pending_forms(&mut self, mut be: Box<dyn Backend>) -> Box<dyn Backend> {
632 for n in self.pending_forms.pop().unwrap_or_default() {
633 be = self.write_box_resource(be, n);
634 }
635 be
636 }
637
638 fn out_image(&mut self, w: &mut Walker, index: i32, h: i32, v: i32, width: i32, height: i32) {
640 let (x, y) = w.pos(h, v);
641 if let Some(be) = w.backend.as_deref_mut() {
642 be.image(self, index as u32, x, y, width, height);
643 if self.mark_image_written(index as u32) {
644 self.pdf.page_images.push(index as u32);
645 }
646 }
647 }
648
649 fn out_box(&mut self, w: &mut Walker, p: NodeRef, h: i32, v: i32) {
652 if w.cur_s >= crate::nodes::MAX_NESTING as i32 {
655 return;
656 }
657 if self.nodes.id(p) == id::VLIST {
658 self.vlist_out(w, p, h, v);
659 } else {
660 self.hlist_out(w, p, h, v);
661 }
662 }
663
664 fn enter_box(w: &mut Walker) {
666 w.cur_s += 1;
667 if let Some(d) = w.dvi.as_mut() {
668 if w.cur_s > 0 {
669 d.push(w.cur_s);
670 } else {
671 d.note_depth(w.cur_s);
672 }
673 }
674 }
675
676 fn leave_box(w: &mut Walker) {
678 if w.cur_s > 0
679 && let Some(d) = w.dvi.as_mut()
680 {
681 d.pop();
682 }
683 w.cur_s -= 1;
684 }
685
686 fn hlist_out(&mut self, w: &mut Walker, this_box: NodeRef, mut cur_h: i32, cur_v: i32) {
689 let Some(b) = self.nodes.box_data(this_box).cloned() else {
690 return;
691 };
692 let mut gs = GlueState {
693 cur_g: 0,
694 cur_glue: 0.0,
695 order: b.glue_order,
696 sign: b.glue_sign,
697 set: b.glue_set,
698 };
699 Self::enter_box(w);
700 let base_line = cur_v;
701 let left_edge = cur_h;
702 let ctx = crate::pdfship::BoxCtx {
703 left: left_edge,
704 base: base_line,
705 width: b.width,
706 height: b.height,
707 depth: b.depth,
708 };
709 if !w.doing_leaders {
710 self.ship_continue_links(w, &ctx);
711 }
712 if w.sync {
713 let f = self.sync_of(this_box);
714 self.synctex
715 .box_open(true, f, cur_h, cur_v, [b.width, b.height, b.depth]);
716 }
717 let places = self.rtl_places(b.head, b.dir == 1, &gs);
720 let mut i = 0;
721 let mut p = b.head;
722 while !p.is_null() {
723 let next = self.nodes.next(p);
724 let rtl = match &places {
725 Some(pl) => {
726 cur_h = left_edge + pl[i].0;
727 i += 1;
728 pl[i - 1].1
729 }
730 None => false,
731 };
732 if w.sync {
733 self.sync_hlist_node(p, cur_h, base_line);
734 }
735 let n = self.nodes.get(p);
736 let n_id = n.id;
737 match &n.data {
738 NodeData::Glyph(g) => {
739 let (font, chr, xo, yo, ef) =
740 (g.font, g.chr, g.xoffset, g.yoffset, g.expansion_factor);
741 if !self.pdf.map_note_done
742 && w.backend.is_some()
743 && self.fonts.char_metrics(font, chr).is_some()
744 {
745 self.pdf.map_note_done = true;
749 self.print_map_file_note("pdftex.map");
750 }
751 if self.fonts.char_metrics(font, chr).is_none() {
752 self.shipout_lost_char(font, chr);
753 }
754 let x = if rtl { cur_h - xo } else { cur_h + xo };
756 self.out_glyph(w, font, chr, x, base_line - yo, ef);
757 cur_h +=
758 crate::expansion::expanded_width(self.font_char_dimen(font, chr, 0), ef);
759 }
760 NodeData::Box(inner) => {
761 let (iw, shift, head) = (inner.width, inner.shift, inner.head);
762 if !head.is_null() {
763 self.out_box(w, p, cur_h, base_line + shift);
764 } else if w.sync {
765 let size = [iw, inner.height, inner.depth];
766 let f = self.sync_of(p);
767 self.synctex
768 .void_box(n_id == id::HLIST, f, cur_h, base_line, size);
769 }
770 cur_h += iw;
771 }
772 NodeData::Rule {
773 width,
774 depth,
775 height,
776 index,
777 ..
778 } => {
779 let (wd, ht, dpt, idx) = (*width, *height, *depth, *index);
780 if n.subtype == crate::image::IMAGE_RULE {
781 self.out_image(w, idx, cur_h, base_line + dpt, wd, ht + dpt);
782 } else if n.subtype == crate::image::BOX_RESOURCE_RULE {
783 self.out_box_resource(w, idx, cur_h, base_line + dpt, wd, ht + dpt);
784 } else {
785 Self::hlist_rule(w, cur_h, base_line, wd, ht, dpt, &b);
786 }
787 cur_h += wd;
788 if w.sync {
789 let f = self.sync_of(p);
790 self.synctex.rule(f, cur_h, base_line, [wd, ht, dpt]);
791 }
792 }
793 NodeData::Glue { spec, leader } => {
794 let (spec, leader, sub) = (*spec, *leader, n.subtype);
795 let wd = gs.advance(&spec);
796 let box_leaders = sub >= glue_sub::A_LEADERS
797 && !leader.is_null()
798 && self.nodes.id(leader) != id::RULE;
799 if sub >= glue_sub::A_LEADERS && !leader.is_null() {
800 self.hlist_leaders(w, leader, sub, cur_h, left_edge, base_line, wd, &b);
801 }
802 cur_h += wd;
803 if w.sync && !box_leaders {
804 let f = self.sync_of(p);
805 self.synctex.glue(f, cur_h, base_line);
806 }
807 }
808 NodeData::Kern {
809 kern,
810 expansion_factor,
811 } => {
812 let wd = kern + expansion_factor;
813 if w.sync {
814 let f = self.sync_of(p);
817 self.synctex.kern(f, *kern);
818 }
819 cur_h += wd;
820 }
821 NodeData::Math { surround, glue } => {
822 let g = *glue;
823 if w.sync {
824 let f = self.sync_of(p);
825 self.synctex.math(f, cur_h, base_line);
826 }
827 cur_h += surround;
828 if g != Glue::ZERO {
829 cur_h += gs.advance(&g);
830 }
831 }
832 NodeData::MarginKern { width, .. } => cur_h += width,
833 NodeData::Whatsit(_) => self.out_what(w, p, cur_h, base_line, &ctx),
834 NodeData::Disc { replace, .. } => {
835 let mut items = Vec::new();
839 let mut q = *replace;
840 while !q.is_null() {
841 items.push(q);
842 q = self.nodes.next(q);
843 }
844 let start = cur_h;
845 let total: i32 = items.iter().map(|&q| self.replace_item_width(q)).sum();
846 for q in items {
847 if rtl {
848 cur_h = start + total
850 - items_before_width(self, *replace, q)
851 - self.replace_item_width(q);
852 }
853 if let NodeData::Glyph(g) = &self.nodes.get(q).data {
854 let (font, chr, ef) = (g.font, g.chr, g.expansion_factor);
855 self.out_glyph(w, font, chr, cur_h, base_line, ef);
856 if w.sync {
857 self.synctex.glyph();
858 }
859 cur_h += crate::expansion::expanded_width(
860 self.font_char_dimen(font, chr, 0),
861 ef,
862 );
863 } else if let NodeData::Kern {
864 kern,
865 expansion_factor,
866 } = &self.nodes.get(q).data
867 {
868 let wd = kern + expansion_factor;
869 if w.sync {
870 self.synctex.end_run(cur_h, base_line);
871 let f = self.sync_of(q);
872 self.synctex.kern(f, *kern);
873 }
874 cur_h += wd;
875 }
876 }
877 }
878 _ => {}
879 }
880 p = next;
881 }
882 if places.is_some() {
883 cur_h = left_edge + b.width;
884 }
885 if !w.doing_leaders {
886 self.ship_end_box_links(w, &ctx);
887 }
888 if w.sync {
889 self.synctex.end_run(cur_h, base_line);
890 let f = self.sync_of(this_box);
891 self.synctex.box_close(true, f, cur_h, base_line);
892 }
893 Self::leave_box(w);
894 }
895
896 fn replace_item_width(&self, q: NodeRef) -> i32 {
898 match &self.nodes.get(q).data {
899 NodeData::Glyph(g) => crate::expansion::expanded_width(
900 self.font_char_dimen(g.font, g.chr, 0),
901 g.expansion_factor,
902 ),
903 NodeData::Kern {
904 kern,
905 expansion_factor,
906 } => kern + expansion_factor,
907 _ => 0,
908 }
909 }
910
911 fn hlist_widths(&self, head: NodeRef, gs: &GlueState) -> Vec<i32> {
914 let mut gs = gs.clone();
915 let mut out = Vec::new();
916 let mut p = head;
917 while !p.is_null() {
918 let n = self.nodes.get(p);
919 let w = match &n.data {
920 NodeData::Glyph(g) => crate::expansion::expanded_width(
921 self.font_char_dimen(g.font, g.chr, 0),
922 g.expansion_factor,
923 ),
924 NodeData::Box(inner) => inner.width,
925 NodeData::Rule { width, .. } => *width,
926 NodeData::Glue { spec, .. } => gs.advance(spec),
927 NodeData::Kern {
928 kern,
929 expansion_factor,
930 } => kern + expansion_factor,
931 NodeData::Math { surround, glue } => {
932 surround
933 + if *glue != Glue::ZERO {
934 gs.advance(glue)
935 } else {
936 0
937 }
938 }
939 NodeData::MarginKern { width, .. } => *width,
940 NodeData::Disc { replace, .. } => {
941 let mut q = *replace;
942 let mut w = 0;
943 while !q.is_null() {
944 w += self.replace_item_width(q);
945 q = self.nodes.next(q);
946 }
947 w
948 }
949 _ => 0,
950 };
951 out.push(w);
952 p = n.next;
953 }
954 out
955 }
956
957 fn rtl_places(&self, head: NodeRef, box_rtl: bool, gs: &GlueState) -> Option<Vec<(i32, bool)>> {
966 let mut has_dirs = false;
967 let mut p = head;
968 while !p.is_null() {
969 if self.nodes.id(p) == id::DIR {
970 has_dirs = true;
971 break;
972 }
973 p = self.nodes.next(p);
974 }
975 if !box_rtl && !has_dirs {
976 return None;
977 }
978 let widths = self.hlist_widths(head, gs);
979 enum Item {
980 Node(usize),
981 Seg(bool, Vec<Item>),
982 }
983 let mut stack: Vec<(bool, Vec<Item>)> = vec![(box_rtl, Vec::new())];
984 let close = |stack: &mut Vec<(bool, Vec<Item>)>| {
985 let (rtl, items) = stack.pop().expect("open segment");
986 stack
987 .last_mut()
988 .expect("outer segment")
989 .1
990 .push(Item::Seg(rtl, items));
991 };
992 let mut p = head;
993 let mut k = 0;
994 while !p.is_null() {
995 if let NodeData::Dir { dir, .. } = self.nodes.data(p) {
996 let begin = self.nodes.subtype(p) != crate::dirs::dir_sub::END;
997 if !begin && stack.len() > 1 {
998 stack.last_mut().expect("segment").1.push(Item::Node(k));
1000 close(&mut stack);
1001 } else if begin {
1002 stack.push((*dir == 1, vec![Item::Node(k)]));
1003 } else {
1004 stack.last_mut().expect("segment").1.push(Item::Node(k));
1005 }
1006 } else {
1007 stack.last_mut().expect("segment").1.push(Item::Node(k));
1008 }
1009 k += 1;
1010 p = self.nodes.next(p);
1011 }
1012 while stack.len() > 1 {
1013 close(&mut stack);
1014 }
1015 fn width(items: &[Item], widths: &[i32]) -> i32 {
1016 items
1017 .iter()
1018 .map(|it| match it {
1019 Item::Node(i) => widths[*i],
1020 Item::Seg(_, sub) => width(sub, widths),
1021 })
1022 .sum()
1023 }
1024 fn place(rtl: bool, items: &[Item], left: i32, widths: &[i32], out: &mut [(i32, bool)]) {
1025 let mut x = if rtl {
1026 left + width(items, widths)
1027 } else {
1028 left
1029 };
1030 for it in items {
1031 match it {
1032 Item::Node(i) => {
1033 if rtl {
1034 x -= widths[*i];
1035 out[*i] = (x, true);
1036 } else {
1037 out[*i] = (x, false);
1038 x += widths[*i];
1039 }
1040 }
1041 Item::Seg(r, sub) => {
1042 let w = width(sub, widths);
1043 if rtl {
1044 x -= w;
1045 place(*r, sub, x, widths, out);
1046 } else {
1047 place(*r, sub, x, widths, out);
1048 x += w;
1049 }
1050 }
1051 }
1052 }
1053 }
1054 let (rtl, items) = stack.pop().expect("box segment");
1055 let mut out = vec![(0, false); widths.len()];
1056 place(rtl, &items, 0, &widths, &mut out);
1057 Some(out)
1058 }
1059
1060 #[inline]
1063 fn sync_hlist_node(&mut self, p: NodeRef, cur_h: i32, base_line: i32) {
1064 match self.nodes.id(p) {
1065 id::GLYPH => self.synctex.glyph(),
1066 id::MARK | id::INS | id::ADJUST | id::BOUNDARY | id::LOCAL_PAR => {}
1067 _ => self.synctex.end_run(cur_h, base_line),
1068 }
1069 }
1070
1071 fn hlist_rule(
1074 w: &mut Walker,
1075 cur_h: i32,
1076 base_line: i32,
1077 wd: i32,
1078 ht: i32,
1079 dpt: i32,
1080 this_box: &crate::nodes::BoxData,
1081 ) {
1082 let ht = if ht == RUNNING { this_box.height } else { ht };
1083 let dpt = if dpt == RUNNING { this_box.depth } else { dpt };
1084 let total = ht + dpt;
1085 if total > 0 && wd > 0 {
1086 Self::out_rule(w, cur_h, base_line + dpt, wd, total);
1087 }
1088 }
1089
1090 fn vlist_out(&mut self, w: &mut Walker, this_box: NodeRef, cur_h: i32, cur_v: i32) {
1092 let Some(b) = self.nodes.box_data(this_box).cloned() else {
1093 return;
1094 };
1095 let mut gs = GlueState {
1096 cur_g: 0,
1097 cur_glue: 0.0,
1098 order: b.glue_order,
1099 sign: b.glue_sign,
1100 set: b.glue_set,
1101 };
1102 Self::enter_box(w);
1103 let left_edge = cur_h;
1104 let ctx = crate::pdfship::BoxCtx {
1105 left: left_edge,
1106 base: cur_v,
1107 width: b.width,
1108 height: b.height,
1109 depth: b.depth,
1110 };
1111 let mut cur_v = cur_v - b.height;
1112 let top_edge = cur_v;
1113 if w.sync {
1114 let f = self.sync_of(this_box);
1115 self.synctex
1116 .box_open(false, f, cur_h, cur_v, [b.width, b.height, b.depth]);
1117 }
1118 let mut p = b.head;
1119 while !p.is_null() {
1120 let next = self.nodes.next(p);
1121 let n = self.nodes.get(p);
1122 match &n.data {
1123 NodeData::Box(inner) => {
1124 let (ih, idp, shift, head) =
1125 (inner.height, inner.depth, inner.shift, inner.head);
1126 if head.is_null() {
1127 cur_v += ih + idp;
1128 if w.sync {
1129 let size = [inner.width, ih, idp];
1130 let hlist = n.id == id::HLIST;
1131 let f = self.sync_of(p);
1132 self.synctex.void_box(hlist, f, left_edge, cur_v, size);
1133 }
1134 } else {
1135 cur_v += ih;
1136 self.out_box(w, p, left_edge + shift, cur_v);
1137 cur_v += idp;
1138 }
1139 }
1140 NodeData::Rule {
1141 width,
1142 depth,
1143 height,
1144 index,
1145 ..
1146 } => {
1147 let wd = if *width == RUNNING { b.width } else { *width };
1148 let total = height + depth;
1149 let idx = *index;
1150 cur_v += total;
1151 if n.subtype == crate::image::IMAGE_RULE {
1152 self.out_image(w, idx, left_edge, cur_v, wd, total);
1153 } else if n.subtype == crate::image::BOX_RESOURCE_RULE {
1154 self.out_box_resource(w, idx, left_edge, cur_v, wd, total);
1155 } else if total > 0 && wd > 0 {
1156 Self::out_rule(w, left_edge, cur_v, wd, total);
1157 }
1158 }
1159 NodeData::Glue { spec, leader } => {
1160 let (spec, leader, sub) = (*spec, *leader, n.subtype);
1161 let ht = gs.advance(&spec);
1162 if sub >= glue_sub::A_LEADERS && !leader.is_null() {
1163 cur_v =
1164 self.vlist_leaders(w, leader, sub, cur_v, left_edge, top_edge, ht, &b);
1165 } else {
1166 cur_v += ht;
1167 }
1168 }
1169 NodeData::Kern { kern, .. } => cur_v += kern,
1170 NodeData::Whatsit(_) => self.out_what(w, p, left_edge, cur_v, &ctx),
1171 _ => {}
1172 }
1173 p = next;
1174 }
1175 if w.sync {
1176 let f = self.sync_of(this_box);
1177 self.synctex.box_close(false, f, left_edge, cur_v);
1178 }
1179 Self::leave_box(w);
1180 }
1181
1182 fn leader_start(sub: u16, cur: i32, edge0: i32, size: i32, step: i32) -> (i32, i32) {
1186 if sub == glue_sub::C_LEADERS || sub == glue_sub::X_LEADERS {
1187 let lq = size / step;
1188 let lr = size % step;
1189 if sub == glue_sub::C_LEADERS {
1190 (cur + lr / 2, 0)
1191 } else {
1192 let lx = lr / (lq + 1);
1193 (cur + (lr - (lq - 1) * lx) / 2, lx)
1194 }
1195 } else {
1196 let mut c = edge0 + step * ((cur - edge0) / step);
1197 if c < cur {
1198 c += step;
1199 }
1200 (c, 0)
1201 }
1202 }
1203
1204 #[allow(clippy::too_many_arguments)]
1206 fn hlist_leaders(
1207 &mut self,
1208 w: &mut Walker,
1209 leader: NodeRef,
1210 sub: u16,
1211 cur_h: i32,
1212 left_edge: i32,
1213 base_line: i32,
1214 wd: i32,
1215 this_box: &crate::nodes::BoxData,
1216 ) {
1217 let lb = match &self.nodes.get(leader).data {
1218 NodeData::Rule { height, depth, .. } => {
1219 let (ht, dpt) = (*height, *depth);
1220 Self::hlist_rule(w, cur_h, base_line, wd, ht, dpt, this_box);
1221 return;
1222 }
1223 NodeData::Box(lb) => lb.clone(),
1224 _ => return,
1225 };
1226 let leader_wd = lb.width;
1227 if leader_wd <= 0 || wd <= 0 {
1228 return;
1229 }
1230 let rule_wd = wd + 10; let edge = cur_h + rule_wd;
1232 let (mut h, lx) = Self::leader_start(sub, cur_h, left_edge, rule_wd, leader_wd);
1233 while h + leader_wd <= edge {
1236 let outer = w.doing_leaders;
1237 w.doing_leaders = true;
1238 self.out_box(w, leader, h, base_line + lb.shift);
1239 w.doing_leaders = outer;
1240 h += leader_wd + lx;
1241 }
1242 }
1243
1244 #[allow(clippy::too_many_arguments)]
1247 fn vlist_leaders(
1248 &mut self,
1249 w: &mut Walker,
1250 leader: NodeRef,
1251 sub: u16,
1252 cur_v: i32,
1253 left_edge: i32,
1254 top_edge: i32,
1255 ht: i32,
1256 this_box: &crate::nodes::BoxData,
1257 ) -> i32 {
1258 let lb = match &self.nodes.get(leader).data {
1259 NodeData::Rule { width, .. } => {
1260 let wd = if *width == RUNNING {
1261 this_box.width
1262 } else {
1263 *width
1264 };
1265 if ht > 0 && wd > 0 {
1266 Self::out_rule(w, left_edge, cur_v + ht, wd, ht);
1267 }
1268 return cur_v + ht;
1269 }
1270 NodeData::Box(lb) => lb.clone(),
1271 _ => return cur_v + ht,
1272 };
1273 let leader_ht = lb.height + lb.depth;
1274 if leader_ht <= 0 || ht <= 0 {
1275 return cur_v + ht;
1276 }
1277 let rule_ht = ht + 10; let edge = cur_v + rule_ht;
1279 let (mut v, lx) = Self::leader_start(sub, cur_v, top_edge, rule_ht, leader_ht);
1280 while v + leader_ht <= edge {
1281 let outer = w.doing_leaders;
1282 w.doing_leaders = true;
1283 self.out_box(w, leader, left_edge + lb.shift, v + lb.height);
1284 w.doing_leaders = outer;
1285 v += leader_ht + lx;
1286 }
1287 edge - 10
1288 }
1289
1290 fn out_what(
1292 &mut self,
1293 w: &mut Walker,
1294 p: NodeRef,
1295 h: i32,
1296 v: i32,
1297 ctx: &crate::pdfship::BoxCtx,
1298 ) {
1299 let NodeData::Whatsit(wh) = &self.nodes.get(p).data else {
1300 return;
1301 };
1302 let (x, y) = w.pos(h, v);
1303 match (**wh).clone() {
1304 Whatsit::Open { stream, name } if !w.doing_leaders => {
1305 self.open_write_stream(stream as i32, &name)
1306 }
1307 Whatsit::Write { stream, data } if !w.doing_leaders => self.write_out(stream, &data),
1308 Whatsit::Close { stream } if !w.doing_leaders => self.close_write_stream(stream as i32),
1309 Whatsit::Special { data } => {
1310 if let Some(d) = w.dvi.as_mut() {
1311 d.special(w.dvi_h + h, w.dvi_v + v, &data);
1312 } else if let Some(be) = w.backend.as_deref_mut() {
1313 if data.len() >= 4 && data[..4].eq_ignore_ascii_case(b"pdf:") {
1317 let rest = &data[4..];
1318 let (mode, text) = [
1319 (&b"direct:"[..], 2),
1320 (b"page:", 1),
1321 (b"raw:", 3),
1322 (b"text:", 4),
1323 (b"origin:", 0),
1324 ]
1325 .iter()
1326 .find_map(|(k, m)| rest.strip_prefix(*k).map(|t| (*m, t)))
1327 .unwrap_or((crate::pdfext::literal_mode::ORIGIN, rest));
1328 be.literal(mode, text, x, y);
1329 } else {
1330 self.printer.print_nl("Non-PDF special ignored!");
1331 }
1332 }
1333 }
1334 Whatsit::PdfLiteral { mode, data, .. } => {
1335 let bytes = match data {
1336 crate::nodes::LateData::Bytes(b)
1337 | crate::nodes::LateData::Lua { data: b, .. } => b,
1338 crate::nodes::LateData::Tokens(t) => {
1339 let toks = self.expand_late_tokens(&t);
1340 self.print_to_string(|e| e.show_token_list(&toks, 0, toks.len(), i64::MAX))
1341 }
1342 };
1343 if let Some(be) = w.backend.as_deref_mut() {
1344 be.literal(mode, &bytes, x, y);
1345 }
1346 }
1347 Whatsit::LateLua { data, .. } => {
1348 let code = match data {
1349 crate::nodes::LateData::Bytes(b)
1350 | crate::nodes::LateData::Lua { data: b, .. } => b,
1351 crate::nodes::LateData::Tokens(t) => {
1352 let toks = self.expand_late_tokens(&t);
1353 self.lua_code(&toks)
1354 }
1355 };
1356 self.pdf.pos = (x, y);
1359 let outer = self.pdf.late.replace(Default::default());
1360 self.run_lua(&code);
1361 let ctx = std::mem::replace(&mut self.pdf.late, outer).unwrap_or_default();
1362 if let Some(be) = w.backend.as_deref_mut() {
1363 for (mode, text) in &ctx.prints {
1364 be.literal(*mode, text, x, y);
1365 }
1366 }
1367 self.pdf
1368 .page
1369 .annots
1370 .extend(ctx.annots.into_iter().map(|a| (a, None)));
1371 }
1372 Whatsit::PdfRefobj { objnum } if !w.doing_leaders => self.ship_refobj(w, objnum),
1373 Whatsit::PdfAnnot { dims, objnum, data } if !w.doing_leaders => {
1374 self.ship_annot(w, ctx, h, v, dims, objnum, &data)
1375 }
1376 Whatsit::PdfStartLink {
1377 dims,
1378 objnum,
1379 attr,
1380 action,
1381 } if !w.doing_leaders => {
1382 self.ship_start_link(w, ctx, h, v, dims, objnum, &attr, &action)
1383 }
1384 Whatsit::PdfEndLink if !w.doing_leaders => self.ship_end_link(w, h, v),
1385 Whatsit::PdfDest {
1386 id,
1387 view,
1388 structure,
1389 } if !w.doing_leaders => self.ship_dest(w, ctx, h, v, &id, &view, structure),
1390 Whatsit::SavePos => {
1391 self.last_x_pos = x;
1392 self.last_y_pos = y;
1393 }
1394 Whatsit::PdfSave => {
1395 if let Some(be) = w.backend.as_deref_mut() {
1396 be.save(x, y);
1397 }
1398 }
1399 Whatsit::PdfRestore => {
1400 if let Some(be) = w.backend.as_deref_mut() {
1401 be.restore(x, y);
1402 }
1403 }
1404 Whatsit::PdfSetmatrix { data } => {
1405 if let Some(be) = w.backend.as_deref_mut() {
1406 be.set_matrix(x, y, &data);
1407 }
1408 }
1409 Whatsit::PdfColorstack { stack, cmd, data } => {
1410 let out = self.colorstack_op(stack, cmd, data);
1411 if let (Some((mode, lit)), Some(be)) = (out, w.backend.as_deref_mut()) {
1412 be.literal(mode, &lit, x, y);
1413 }
1414 }
1415 _ => {}
1416 }
1417 }
1418
1419 fn colorstack_op(&mut self, stack: i32, cmd: u8, data: Vec<u8>) -> Option<(u8, Vec<u8>)> {
1421 use crate::pdfext::colorstack_cmd as c;
1422 self.pdf_ensure_colorstacks();
1423 let def = self.pdf.colorstacks.get(stack.max(0) as usize).cloned()?;
1424 let st = self
1425 .colorstack_values
1426 .entry(stack)
1427 .or_insert_with(|| vec![def.init.clone()]);
1428 match cmd {
1429 c::SET => {
1430 if let Some(top) = st.last_mut() {
1431 *top = data;
1432 } else {
1433 st.push(data);
1434 }
1435 }
1436 c::PUSH => st.push(data),
1437 c::POP => {
1438 if st.len() > 1 {
1439 st.pop();
1440 }
1441 }
1442 _ => {}
1443 }
1444 let lit = st.last().cloned().unwrap_or_default();
1445 Some((def.mode, lit))
1446 }
1447
1448 fn expand_late_tokens(&mut self, list: &crate::token::TokenList) -> Vec<crate::token::Token> {
1451 use crate::token::Token;
1452 use std::rc::Rc;
1453 self.ins_list(Rc::from(vec![
1454 Token::RIGHT_BRACE,
1455 Token::cs(self.cs.end_write),
1456 ]));
1457 self.begin_token_list(list.clone(), crate::input::TokKind::WriteText);
1458 self.ins_list(Rc::from(vec![Token::LEFT_BRACE]));
1459 let toks = self.scan_toks(false, true);
1460 self.get_token();
1461 toks
1462 }
1463
1464 pub fn finish_backend(&mut self) {
1466 if self.dvi.pages() > 0 {
1467 self.finish_dvi();
1468 } else if self.pdf.output_opened && self.eqtb.int(ip::OUTPUTMODE) <= 0 {
1469 let _ = std::fs::write(self.output_path("dvi"), b"");
1471 }
1472 let Some(mut be) = self.backend.take() else {
1473 return;
1474 };
1475 if self.pdf.fatal || self.total_pages == 0 {
1476 be.abandon(self.pdf.fatal);
1477 self.backend = Some(be);
1478 return;
1479 }
1480 if self.eqtb.int(ip::DRAFTMODE) > 0 && self.eqtb.int(ip::OUTPUTMODE) > 0 {
1481 be.abandon(true);
1486 self.backend = Some(be);
1487 self.printer
1488 .print_nl("warning (pdf backend): draftmode enabled, not changing output pdf");
1489 self.printer.print_ln();
1490 return;
1491 }
1492 if self.total_pages > 0 {
1493 self.pdf_finish_document();
1494 self.flush_pending_objects(be.as_mut());
1495 }
1496 if self.total_pages > 0
1497 && let Some(fin) = be.finish(self)
1498 {
1499 for f in &fin.embedded {
1502 let mut text = b"<".to_vec();
1503 text.extend_from_slice(f.as_bytes());
1504 text.push(b'>');
1505 self.printer.print_lua_bytes(&text);
1506 }
1507 self.pdf.objstm_stats = fin.objstm;
1508 self.print_output_written(&fin.file, self.total_pages, fin.size);
1509 }
1510 self.backend = Some(be);
1511 }
1512
1513 pub(crate) fn output_path(&self, ext: &str) -> std::path::PathBuf {
1515 let job =
1516 String::from_utf8_lossy(self.job_name.as_deref().unwrap_or(b"texput")).into_owned();
1517 let name = format!("{job}.{ext}");
1518 match &self.files.output_dir {
1519 Some(d) => d.join(&name),
1520 None => std::path::PathBuf::from(&name),
1521 }
1522 }
1523
1524 fn finish_dvi(&mut self) {
1526 let mut dvi = std::mem::take(&mut self.dvi);
1527 let pages = dvi.pages();
1528 let data = dvi.finish(self);
1529 let path = self.output_path("dvi");
1530 let name = path
1531 .file_name()
1532 .map(|n| n.to_string_lossy().into_owned())
1533 .unwrap_or_default();
1534 if std::fs::write(&path, &data).is_err() {
1535 self.printer.print_nl("! I can't write on file `");
1536 self.printer.print_bytes(name.as_bytes());
1537 self.printer.print_str("'.");
1538 return;
1539 }
1540 self.print_output_written(&name, pages, data.len() as u64);
1541 }
1542
1543 fn print_output_written(&mut self, name: &str, pages: u32, size: u64) {
1545 self.output_written = Some((name.to_owned(), pages, size));
1546 self.printer.print_nl("Output written on ");
1547 self.printer.print_bytes(name.as_bytes());
1548 self.printer.print_str(" (");
1549 self.printer.print_int(i64::from(pages));
1550 self.printer
1551 .print_str(if pages == 1 { " page" } else { " pages" });
1552 self.printer.print_str(", ");
1553 self.printer.print_int(size as i64);
1554 self.printer.print_str(" bytes).");
1555 }
1556}
1557
1558pub(crate) struct Walker {
1561 backend: Option<Box<dyn Backend>>,
1563 dvi: Option<crate::dvi::DviWriter>,
1565 h_origin: i32,
1566 v_origin: i32,
1567 page_height: i32,
1568 dvi_h: i32,
1570 dvi_v: i32,
1571 pub(crate) cur_s: i32,
1573 doing_leaders: bool,
1575 pub(crate) page: bool,
1578 sync: bool,
1580}
1581
1582impl Walker {
1583 pub(crate) fn pos(&self, h: i32, v: i32) -> (i32, i32) {
1586 (self.h_origin + h, self.page_height - (self.v_origin + v))
1587 }
1588}