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