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 pub fn ship_out(&mut self, p: NodeRef) {
183 self.pdf.page_annots.clear();
184 let tracing = self.eqtb.int(ip::TRACINGOUTPUT) > 0;
185 if tracing {
186 self.printer.print_nl("");
187 self.printer.print_ln();
188 self.printer.print_str("Completed box being shipped out");
189 }
190 if self.printer.term_offset > self.printer.max_print_line.saturating_sub(9) {
191 self.printer.print_ln();
192 } else if self.printer.term_offset > 0 || self.printer.file_offset > 0 {
193 self.printer.print_char(' ' as u32);
194 }
195 self.printer.print_char('[' as u32);
196 let mut j = 9;
197 while j > 0 && self.eqtb.count[j].val == 0 {
198 j -= 1;
199 }
200 for k in 0..=j {
201 self.printer.print_int(self.eqtb.count[k].val.into());
202 if k < j {
203 self.printer.print_char('.' as u32);
204 }
205 }
206 self.printer.update_terminal();
207 if tracing {
208 self.printer.print_char(']' as u32);
209 self.begin_diagnostic();
210 self.show_box(p);
211 self.end_diagnostic(true);
212 }
213 self.ship_box(p);
214 if !tracing {
215 self.printer.print_char(']' as u32);
216 }
217 self.dead_cycles = 0;
218 self.printer.update_terminal();
219 self.nodes.flush_list(p);
220 }
221
222 fn ship_box(&mut self, p: NodeRef) {
224 let Some(b) = self.nodes.box_data(p).cloned() else {
225 return;
226 };
227 let pdf = self.eqtb.int(ip::OUTPUTMODE) > 0;
228 let h_origin = self.eqtb.dimen(crate::backend::H_ORIGIN);
230 let v_origin = self.eqtb.dimen(crate::backend::V_ORIGIN);
231 let h_off = self.eqtb.dimen(dp::HOFFSET);
232 let v_off = self.eqtb.dimen(dp::VOFFSET);
233 let mut width = self.eqtb.dimen(dp::PAGEWIDTH);
234 let mut height = self.eqtb.dimen(dp::PAGEHEIGHT);
235 if width <= 0 {
236 width = b.width + 2 * (h_origin + h_off);
237 }
238 if height <= 0 {
239 height = b.height + b.depth + 2 * (v_origin + v_off);
240 }
241 let mut backend = if pdf { self.backend.take() } else { None };
242 let forms_depth = self.pending_forms.len();
243 self.pending_forms.push(Vec::new());
244 if let Some(be) = backend.as_mut() {
245 self.pdf.page = Default::default();
246 be.begin_page(self, width, height);
247 if let Some(n) = be.current_page_object() {
248 let page = self.total_pages as i32 + 1;
249 self.pdf.page_refs.insert(page, n);
250 self.pdf.kinds.insert(n, crate::pdfext::ObjKind::Page);
251 }
252 let (x, y) = (h_origin + h_off, height - (v_origin + v_off + b.height));
254 for (mode, lit) in self.page_colorstack_literals() {
255 be.literal(mode, &lit, x, y);
256 }
257 }
258 let dvi = if pdf {
259 None
260 } else {
261 let mut d = std::mem::take(&mut self.dvi);
262 d.begin_page(self, (b.width, b.height, b.depth), (h_off, v_off));
263 Some(d)
264 };
265 let mut walker = Walker {
266 backend,
267 dvi,
268 h_origin: h_origin + h_off,
269 v_origin: v_origin + v_off,
270 page_height: height,
271 dvi_h: h_off,
272 dvi_v: v_off,
273 cur_s: -1,
274 doing_leaders: false,
275 page: true,
276 };
277 let cur_h = 0;
278 let cur_v = b.height;
279 if self.nodes.id(p) == id::VLIST {
280 self.vlist_out(&mut walker, p, cur_h, cur_v);
281 } else {
282 self.hlist_out(&mut walker, p, cur_h, cur_v);
283 }
284 if let Some(mut be) = walker.backend.take() {
285 self.flush_pending_objects(be.as_mut());
286 self.collect_page_texts();
287 let items = std::mem::take(&mut self.pdf.page);
288 be.close_page_contents(self, &items);
289 self.pdf.page = items;
290 self.resolve_link_actions();
291 let items = std::mem::take(&mut self.pdf.page);
292 be.page_items(self, &items);
293 be.end_page(self);
296 let mut be = self.write_pending_forms(be);
297 be.close_page_resources(self);
298 self.flush_pending_objects(be.as_mut());
299 self.backend = Some(be);
300 for n in std::mem::take(&mut self.pdf.page_images) {
301 self.print_image_name(n);
302 }
303 }
304 self.pending_forms.truncate(forms_depth);
305 if let Some(mut d) = walker.dvi.take() {
306 d.end_page();
307 self.dvi = d;
308 }
309 self.total_pages += 1;
310 }
311
312 fn flush_pending_objects(&mut self, be: &mut dyn Backend) {
314 for obj in std::mem::take(&mut self.pdf.pending) {
315 be.write_object(self, &obj);
316 }
317 }
318
319 pub(crate) fn print_map_file_note(&mut self, name: &str) {
322 let path = self
323 .kpse
324 .as_ref()
325 .and_then(|k| k.borrow_mut().find_file(name, retex_kpath::FileFormat::Map));
326 if let Some(path) = path {
327 let mut text = b"{".to_vec();
329 text.extend_from_slice(path.to_string_lossy().as_bytes());
330 text.push(b'}');
331 self.printer.print_lua_bytes(&text);
332 }
333 }
334
335 fn out_glyph(&self, w: &mut Walker, font: u32, chr: u32, h: i32, v: i32, ef: i32) {
338 self.out_glyph_at_depth(w, font, chr, h, v, ef, 0);
339 }
340
341 #[allow(clippy::too_many_arguments)]
343 fn out_glyph_at_depth(
344 &self,
345 w: &mut Walker,
346 font: u32,
347 chr: u32,
348 h: i32,
349 v: i32,
350 ef: i32,
351 depth: u32,
352 ) {
353 if let Some(vf) = &self.font(font).vf
354 && let Some(cmds) = vf.packets.get(&chr)
355 {
356 if depth < 8 {
357 self.out_packet(w, vf, cmds, h, v, ef, depth);
358 }
359 return;
360 }
361 let (x, y) = w.pos(h, v);
362 if let Some(be) = w.backend.as_deref_mut() {
363 if ef == 0 {
364 be.glyph(self, font, chr, x, y);
365 } else {
366 be.glyph_expanded(self, font, chr, x, y, ef);
367 }
368 }
369 if let Some(d) = w.dvi.as_mut()
370 && self.font(font).chars.contains_key(&chr)
371 {
372 let wd = crate::expansion::expanded_width(self.font_char_dimen(font, chr, 0), ef);
373 d.glyph(self, font, chr, w.dvi_h + h, w.dvi_v + v, wd);
374 }
375 }
376
377 #[allow(clippy::too_many_arguments)]
383 fn out_packet(
384 &self,
385 w: &mut Walker,
386 vf: &crate::fonts::VirtualFont,
387 cmds: &[retex_font::vf::Command],
388 mut h: i32,
389 mut v: i32,
390 ef: i32,
391 depth: u32,
392 ) {
393 use retex_font::vf::Command;
394 let mut f = vf.fonts.first().copied().unwrap_or(0);
395 let mut stack: Vec<(i32, i32)> = Vec::new();
396 for c in cmds {
397 match c {
398 Command::Font(i) => f = vf.fonts.get(*i).copied().unwrap_or(0),
399 Command::Char(c) => {
400 if f != 0 && self.font(f).chars.contains_key(c) {
401 self.out_glyph_at_depth(w, f, *c, h, v, ef, depth + 1);
402 h = h.wrapping_add(crate::expansion::expanded_width(
403 self.font_char_dimen(f, *c, 0),
404 ef,
405 ));
406 }
407 }
408 Command::Rule { height, width } => {
409 if *height > 0 && *width > 0 {
410 Self::out_rule(w, h, v, *width, *height);
411 }
412 h = h.wrapping_add(*width);
413 }
414 Command::Right(x) => h = h.wrapping_add(*x),
415 Command::Down(y) => v = v.wrapping_add(*y),
416 Command::Push => stack.push((h, v)),
417 Command::Pop => {
418 if let Some((sh, sv)) = stack.pop() {
419 (h, v) = (sh, sv);
420 }
421 }
422 Command::Special(_) => {}
423 }
424 }
425 }
426
427 fn out_rule(w: &mut Walker, h: i32, v: i32, width: i32, height: i32) {
430 let (x, y) = w.pos(h, v);
431 if let Some(be) = w.backend.as_deref_mut() {
432 be.rule(x, y, width, height);
433 }
434 if let Some(d) = w.dvi.as_mut() {
435 d.rule(w.dvi_h + h, w.dvi_v + v, width, height);
436 }
437 }
438
439 fn out_box_resource(
443 &mut self,
444 w: &mut Walker,
445 index: i32,
446 h: i32,
447 v: i32,
448 width: i32,
449 height: i32,
450 ) {
451 let n = index as u32;
452 let (x, y) = w.pos(h, v);
453 let Some(mut be) = w.backend.take() else {
454 return;
455 };
456 if self
457 .box_resource_slot(n)
458 .is_some_and(|i| !self.box_resources[i].written)
459 {
460 match self.pending_forms.last_mut() {
461 Some(p) => {
462 if !p.contains(&n) {
463 p.push(n);
464 }
465 }
466 None => be = self.write_box_resource(be, n),
467 }
468 }
469 be.box_form(self, n, x, y, width, height);
470 w.backend = Some(be);
471 }
472
473 pub fn write_box_resource(&mut self, be: Box<dyn Backend>, n: u32) -> Box<dyn Backend> {
477 let Some(slot) = self.box_resource_slot(n) else {
478 return be;
479 };
480 let r = self.box_resources[slot].clone();
481 if r.written {
482 return be;
483 }
484 let mut be = be;
485 let (bw, bh, bd) = r.size;
486 self.pending_forms.push(Vec::new());
487 self.form_objects.push(Vec::new());
488 be.begin_box_form(self, n, (bw, bh, bd), &r.attr, &r.resources);
489 let mut walker = Walker {
490 backend: Some(be),
491 dvi: None,
492 h_origin: 0,
493 v_origin: 0,
494 page_height: bh + bd,
495 dvi_h: 0,
496 dvi_v: 0,
497 cur_s: -1,
498 doing_leaders: false,
499 page: false,
500 };
501 if self.nodes.id(r.list) == id::VLIST {
502 self.vlist_out(&mut walker, r.list, 0, bh);
503 } else {
504 self.hlist_out(&mut walker, r.list, 0, bh);
505 }
506 let mut be = walker.backend.take().expect("the walker keeps the backend");
507 be.end_box_form(self);
508 self.box_resources[slot].written = true;
509 for obj in self.form_objects.pop().unwrap_or_default() {
510 be.write_object(self, &obj);
511 }
512 be = self.write_pending_forms(be);
513 be.close_box_form_resources(self);
514 be
515 }
516
517 fn write_pending_forms(&mut self, mut be: Box<dyn Backend>) -> Box<dyn Backend> {
520 for n in self.pending_forms.pop().unwrap_or_default() {
521 be = self.write_box_resource(be, n);
522 }
523 be
524 }
525
526 fn out_image(&mut self, w: &mut Walker, index: i32, h: i32, v: i32, width: i32, height: i32) {
528 let (x, y) = w.pos(h, v);
529 if let Some(be) = w.backend.as_deref_mut() {
530 be.image(self, index as u32, x, y, width, height);
531 if self.mark_image_written(index as u32) {
532 self.pdf.page_images.push(index as u32);
533 }
534 }
535 }
536
537 fn out_box(&mut self, w: &mut Walker, p: NodeRef, h: i32, v: i32) {
540 if self.nodes.id(p) == id::VLIST {
541 self.vlist_out(w, p, h, v);
542 } else {
543 self.hlist_out(w, p, h, v);
544 }
545 }
546
547 fn enter_box(w: &mut Walker) {
549 w.cur_s += 1;
550 if let Some(d) = w.dvi.as_mut() {
551 if w.cur_s > 0 {
552 d.push(w.cur_s);
553 } else {
554 d.note_depth(w.cur_s);
555 }
556 }
557 }
558
559 fn leave_box(w: &mut Walker) {
561 if w.cur_s > 0
562 && let Some(d) = w.dvi.as_mut()
563 {
564 d.pop();
565 }
566 w.cur_s -= 1;
567 }
568
569 fn hlist_out(&mut self, w: &mut Walker, this_box: NodeRef, mut cur_h: i32, cur_v: i32) {
572 let Some(b) = self.nodes.box_data(this_box).cloned() else {
573 return;
574 };
575 let mut gs = GlueState {
576 cur_g: 0,
577 cur_glue: 0.0,
578 order: b.glue_order,
579 sign: b.glue_sign,
580 set: b.glue_set,
581 };
582 Self::enter_box(w);
583 let base_line = cur_v;
584 let left_edge = cur_h;
585 let ctx = crate::pdfship::BoxCtx {
586 left: left_edge,
587 base: base_line,
588 width: b.width,
589 height: b.height,
590 depth: b.depth,
591 };
592 if !w.doing_leaders {
593 self.ship_continue_links(w, &ctx);
594 }
595 let mut p = b.head;
596 while !p.is_null() {
597 let next = self.nodes.next(p);
598 let n = self.nodes.get(p);
599 match &n.data {
600 NodeData::Glyph(g) => {
601 let (font, chr, xo, yo, ef) =
602 (g.font, g.chr, g.xoffset, g.yoffset, g.expansion_factor);
603 if !self.pdf.map_note_done && w.backend.is_some() {
604 self.pdf.map_note_done = true;
607 self.print_map_file_note("pdftex.map");
608 }
609 self.out_glyph(w, font, chr, cur_h + xo, base_line - yo, ef);
610 cur_h +=
611 crate::expansion::expanded_width(self.font_char_dimen(font, chr, 0), ef);
612 }
613 NodeData::Box(inner) => {
614 let (iw, shift, head) = (inner.width, inner.shift, inner.head);
615 if !head.is_null() {
616 self.out_box(w, p, cur_h, base_line + shift);
617 }
618 cur_h += iw;
619 }
620 NodeData::Rule {
621 width,
622 depth,
623 height,
624 index,
625 ..
626 } => {
627 let (wd, ht, dpt, idx) = (*width, *height, *depth, *index);
628 if n.subtype == crate::image::IMAGE_RULE {
629 self.out_image(w, idx, cur_h, base_line + dpt, wd, ht + dpt);
630 } else if n.subtype == crate::image::BOX_RESOURCE_RULE {
631 self.out_box_resource(w, idx, cur_h, base_line + dpt, wd, ht + dpt);
632 } else {
633 Self::hlist_rule(w, cur_h, base_line, wd, ht, dpt, &b);
634 }
635 cur_h += wd;
636 }
637 NodeData::Glue { spec, leader } => {
638 let (spec, leader, sub) = (*spec, *leader, n.subtype);
639 let wd = gs.advance(&spec);
640 if sub >= glue_sub::A_LEADERS && !leader.is_null() {
641 self.hlist_leaders(w, leader, sub, cur_h, left_edge, base_line, wd, &b);
642 }
643 cur_h += wd;
644 }
645 NodeData::Kern {
646 kern,
647 expansion_factor,
648 } => cur_h += kern + expansion_factor,
649 NodeData::Math { surround, glue } => {
650 let g = *glue;
651 cur_h += surround;
652 if g != Glue::ZERO {
653 cur_h += gs.advance(&g);
654 }
655 }
656 NodeData::MarginKern { width, .. } => cur_h += width,
657 NodeData::Whatsit(_) => self.out_what(w, p, cur_h, base_line, &ctx),
658 NodeData::Disc { replace, .. } => {
659 let r = *replace;
662 let mut q = r;
663 while !q.is_null() {
664 if let NodeData::Glyph(g) = &self.nodes.get(q).data {
665 let (font, chr, ef) = (g.font, g.chr, g.expansion_factor);
666 self.out_glyph(w, font, chr, cur_h, base_line, ef);
667 cur_h += crate::expansion::expanded_width(
668 self.font_char_dimen(font, chr, 0),
669 ef,
670 );
671 } else if let NodeData::Kern {
672 kern,
673 expansion_factor,
674 } = &self.nodes.get(q).data
675 {
676 cur_h += kern + expansion_factor;
677 }
678 q = self.nodes.next(q);
679 }
680 }
681 _ => {}
682 }
683 p = next;
684 }
685 if !w.doing_leaders {
686 self.ship_end_box_links(w, &ctx);
687 }
688 Self::leave_box(w);
689 }
690
691 fn hlist_rule(
694 w: &mut Walker,
695 cur_h: i32,
696 base_line: i32,
697 wd: i32,
698 ht: i32,
699 dpt: i32,
700 this_box: &crate::nodes::BoxData,
701 ) {
702 let ht = if ht == RUNNING { this_box.height } else { ht };
703 let dpt = if dpt == RUNNING { this_box.depth } else { dpt };
704 let total = ht + dpt;
705 if total > 0 && wd > 0 {
706 Self::out_rule(w, cur_h, base_line + dpt, wd, total);
707 }
708 }
709
710 fn vlist_out(&mut self, w: &mut Walker, this_box: NodeRef, cur_h: i32, cur_v: i32) {
712 let Some(b) = self.nodes.box_data(this_box).cloned() else {
713 return;
714 };
715 let mut gs = GlueState {
716 cur_g: 0,
717 cur_glue: 0.0,
718 order: b.glue_order,
719 sign: b.glue_sign,
720 set: b.glue_set,
721 };
722 Self::enter_box(w);
723 let left_edge = cur_h;
724 let ctx = crate::pdfship::BoxCtx {
725 left: left_edge,
726 base: cur_v,
727 width: b.width,
728 height: b.height,
729 depth: b.depth,
730 };
731 let mut cur_v = cur_v - b.height;
732 let top_edge = cur_v;
733 let mut p = b.head;
734 while !p.is_null() {
735 let next = self.nodes.next(p);
736 let n = self.nodes.get(p);
737 match &n.data {
738 NodeData::Box(inner) => {
739 let (ih, idp, shift, head) =
740 (inner.height, inner.depth, inner.shift, inner.head);
741 if head.is_null() {
742 cur_v += ih + idp;
743 } else {
744 cur_v += ih;
745 self.out_box(w, p, left_edge + shift, cur_v);
746 cur_v += idp;
747 }
748 }
749 NodeData::Rule {
750 width,
751 depth,
752 height,
753 index,
754 ..
755 } => {
756 let wd = if *width == RUNNING { b.width } else { *width };
757 let total = height + depth;
758 let idx = *index;
759 cur_v += total;
760 if n.subtype == crate::image::IMAGE_RULE {
761 self.out_image(w, idx, left_edge, cur_v, wd, total);
762 } else if n.subtype == crate::image::BOX_RESOURCE_RULE {
763 self.out_box_resource(w, idx, left_edge, cur_v, wd, total);
764 } else if total > 0 && wd > 0 {
765 Self::out_rule(w, left_edge, cur_v, wd, total);
766 }
767 }
768 NodeData::Glue { spec, leader } => {
769 let (spec, leader, sub) = (*spec, *leader, n.subtype);
770 let ht = gs.advance(&spec);
771 if sub >= glue_sub::A_LEADERS && !leader.is_null() {
772 cur_v =
773 self.vlist_leaders(w, leader, sub, cur_v, left_edge, top_edge, ht, &b);
774 } else {
775 cur_v += ht;
776 }
777 }
778 NodeData::Kern { kern, .. } => cur_v += kern,
779 NodeData::Whatsit(_) => self.out_what(w, p, left_edge, cur_v, &ctx),
780 _ => {}
781 }
782 p = next;
783 }
784 Self::leave_box(w);
785 }
786
787 fn leader_start(sub: u16, cur: i32, edge0: i32, size: i32, step: i32) -> (i32, i32) {
791 if sub == glue_sub::C_LEADERS || sub == glue_sub::X_LEADERS {
792 let lq = size / step;
793 let lr = size % step;
794 if sub == glue_sub::C_LEADERS {
795 (cur + lr / 2, 0)
796 } else {
797 let lx = lr / (lq + 1);
798 (cur + (lr - (lq - 1) * lx) / 2, lx)
799 }
800 } else {
801 let mut c = edge0 + step * ((cur - edge0) / step);
802 if c < cur {
803 c += step;
804 }
805 (c, 0)
806 }
807 }
808
809 #[allow(clippy::too_many_arguments)]
811 fn hlist_leaders(
812 &mut self,
813 w: &mut Walker,
814 leader: NodeRef,
815 sub: u16,
816 cur_h: i32,
817 left_edge: i32,
818 base_line: i32,
819 wd: i32,
820 this_box: &crate::nodes::BoxData,
821 ) {
822 let lb = match &self.nodes.get(leader).data {
823 NodeData::Rule { height, depth, .. } => {
824 let (ht, dpt) = (*height, *depth);
825 Self::hlist_rule(w, cur_h, base_line, wd, ht, dpt, this_box);
826 return;
827 }
828 NodeData::Box(lb) => lb.clone(),
829 _ => return,
830 };
831 let leader_wd = lb.width;
832 if leader_wd <= 0 || wd <= 0 {
833 return;
834 }
835 let rule_wd = wd + 10; let edge = cur_h + rule_wd;
837 let (mut h, lx) = Self::leader_start(sub, cur_h, left_edge, rule_wd, leader_wd);
838 while h + leader_wd <= edge {
841 let outer = w.doing_leaders;
842 w.doing_leaders = true;
843 self.out_box(w, leader, h, base_line + lb.shift);
844 w.doing_leaders = outer;
845 h += leader_wd + lx;
846 }
847 }
848
849 #[allow(clippy::too_many_arguments)]
852 fn vlist_leaders(
853 &mut self,
854 w: &mut Walker,
855 leader: NodeRef,
856 sub: u16,
857 cur_v: i32,
858 left_edge: i32,
859 top_edge: i32,
860 ht: i32,
861 this_box: &crate::nodes::BoxData,
862 ) -> i32 {
863 let lb = match &self.nodes.get(leader).data {
864 NodeData::Rule { width, .. } => {
865 let wd = if *width == RUNNING {
866 this_box.width
867 } else {
868 *width
869 };
870 if ht > 0 && wd > 0 {
871 Self::out_rule(w, left_edge, cur_v + ht, wd, ht);
872 }
873 return cur_v + ht;
874 }
875 NodeData::Box(lb) => lb.clone(),
876 _ => return cur_v + ht,
877 };
878 let leader_ht = lb.height + lb.depth;
879 if leader_ht <= 0 || ht <= 0 {
880 return cur_v + ht;
881 }
882 let rule_ht = ht + 10; let edge = cur_v + rule_ht;
884 let (mut v, lx) = Self::leader_start(sub, cur_v, top_edge, rule_ht, leader_ht);
885 while v + leader_ht <= edge {
886 let outer = w.doing_leaders;
887 w.doing_leaders = true;
888 self.out_box(w, leader, left_edge + lb.shift, v + lb.height);
889 w.doing_leaders = outer;
890 v += leader_ht + lx;
891 }
892 edge - 10
893 }
894
895 fn out_what(
897 &mut self,
898 w: &mut Walker,
899 p: NodeRef,
900 h: i32,
901 v: i32,
902 ctx: &crate::pdfship::BoxCtx,
903 ) {
904 let NodeData::Whatsit(wh) = &self.nodes.get(p).data else {
905 return;
906 };
907 let (x, y) = w.pos(h, v);
908 match (**wh).clone() {
909 Whatsit::Open { stream, name } if !w.doing_leaders => {
910 self.open_write_stream(stream as i32, &name)
911 }
912 Whatsit::Write { stream, data } if !w.doing_leaders => self.write_out(stream, &data),
913 Whatsit::Close { stream } if !w.doing_leaders => self.close_write_stream(stream as i32),
914 Whatsit::Special { data } => {
915 if let Some(d) = w.dvi.as_mut() {
916 d.special(w.dvi_h + h, w.dvi_v + v, &data);
917 }
918 }
919 Whatsit::PdfLiteral { mode, data, .. } => {
920 let bytes = match data {
921 crate::nodes::LateData::Bytes(b) => b,
922 crate::nodes::LateData::Tokens(t) => {
923 let toks = self.expand_late_tokens(&t);
924 self.print_to_string(|e| e.show_token_list(&toks, 0, toks.len(), i64::MAX))
925 }
926 };
927 if let Some(be) = w.backend.as_deref_mut() {
928 be.literal(mode, &bytes, x, y);
929 }
930 }
931 Whatsit::LateLua { data, .. } => {
932 let code = match data {
933 crate::nodes::LateData::Bytes(b) => b,
934 crate::nodes::LateData::Tokens(t) => {
935 let toks = self.expand_late_tokens(&t);
936 self.lua_code(&toks)
937 }
938 };
939 self.pdf.pos = (x, y);
942 let outer = self.pdf.late.replace(Default::default());
943 self.run_lua(&code);
944 let ctx = std::mem::replace(&mut self.pdf.late, outer).unwrap_or_default();
945 if let Some(be) = w.backend.as_deref_mut() {
946 for (mode, text) in &ctx.prints {
947 be.literal(*mode, text, x, y);
948 }
949 }
950 self.pdf
951 .page
952 .annots
953 .extend(ctx.annots.into_iter().map(|a| (a, None)));
954 }
955 Whatsit::PdfRefobj { objnum } if !w.doing_leaders => self.ship_refobj(w, objnum),
956 Whatsit::PdfAnnot { dims, objnum, data } if !w.doing_leaders => {
957 self.ship_annot(w, ctx, h, v, dims, objnum, &data)
958 }
959 Whatsit::PdfStartLink {
960 dims,
961 objnum,
962 attr,
963 action,
964 } if !w.doing_leaders => {
965 self.ship_start_link(w, ctx, h, v, dims, objnum, &attr, &action)
966 }
967 Whatsit::PdfEndLink if !w.doing_leaders => self.ship_end_link(w, h, v),
968 Whatsit::PdfDest { id, view } if !w.doing_leaders => {
969 self.ship_dest(w, ctx, h, v, &id, &view)
970 }
971 Whatsit::SavePos => {
972 self.last_x_pos = x;
973 self.last_y_pos = y;
974 }
975 Whatsit::PdfSave => {
976 if let Some(be) = w.backend.as_deref_mut() {
977 be.save(x, y);
978 }
979 }
980 Whatsit::PdfRestore => {
981 if let Some(be) = w.backend.as_deref_mut() {
982 be.restore(x, y);
983 }
984 }
985 Whatsit::PdfSetmatrix { data } => {
986 if let Some(be) = w.backend.as_deref_mut() {
987 be.set_matrix(x, y, &data);
988 }
989 }
990 Whatsit::PdfColorstack { stack, cmd, data } => {
991 let out = self.colorstack_op(stack, cmd, data);
992 if let (Some((mode, lit)), Some(be)) = (out, w.backend.as_deref_mut()) {
993 be.literal(mode, &lit, x, y);
994 }
995 }
996 _ => {}
997 }
998 }
999
1000 fn colorstack_op(&mut self, stack: i32, cmd: u8, data: Vec<u8>) -> Option<(u8, Vec<u8>)> {
1002 use crate::pdfext::colorstack_cmd as c;
1003 self.pdf_ensure_colorstacks();
1004 let def = self.pdf.colorstacks.get(stack.max(0) as usize).cloned()?;
1005 let st = self
1006 .colorstack_values
1007 .entry(stack)
1008 .or_insert_with(|| vec![def.init.clone()]);
1009 match cmd {
1010 c::SET => {
1011 if let Some(top) = st.last_mut() {
1012 *top = data;
1013 } else {
1014 st.push(data);
1015 }
1016 }
1017 c::PUSH => st.push(data),
1018 c::POP => {
1019 if st.len() > 1 {
1020 st.pop();
1021 }
1022 }
1023 _ => {}
1024 }
1025 let lit = st.last().cloned().unwrap_or_default();
1026 Some((def.mode, lit))
1027 }
1028
1029 fn expand_late_tokens(&mut self, list: &crate::token::TokenList) -> Vec<crate::token::Token> {
1032 use crate::token::Token;
1033 use std::rc::Rc;
1034 self.ins_list(Rc::from(vec![
1035 Token::RIGHT_BRACE,
1036 Token::cs(self.cs.end_write),
1037 ]));
1038 self.begin_token_list(list.clone(), crate::input::TokKind::WriteText);
1039 self.ins_list(Rc::from(vec![Token::LEFT_BRACE]));
1040 let toks = self.scan_toks(false, true);
1041 self.get_token();
1042 toks
1043 }
1044
1045 pub fn finish_backend(&mut self) {
1047 if self.dvi.pages() > 0 {
1048 self.finish_dvi();
1049 } else if self.pdf.output_opened && self.eqtb.int(ip::OUTPUTMODE) <= 0 {
1050 let _ = std::fs::write(self.output_path("dvi"), b"");
1052 }
1053 let Some(mut be) = self.backend.take() else {
1054 return;
1055 };
1056 if self.pdf.fatal || self.total_pages == 0 {
1057 be.abandon(self.pdf.fatal);
1058 self.backend = Some(be);
1059 return;
1060 }
1061 if self.total_pages > 0 {
1062 self.pdf_finish_document();
1063 self.flush_pending_objects(be.as_mut());
1064 }
1065 if self.total_pages > 0
1066 && let Some(fin) = be.finish(self)
1067 {
1068 for f in &fin.embedded {
1071 let mut text = b"<".to_vec();
1072 text.extend_from_slice(f.as_bytes());
1073 text.push(b'>');
1074 self.printer.print_lua_bytes(&text);
1075 }
1076 self.pdf.objstm_stats = fin.objstm;
1077 self.print_output_written(&fin.file, self.total_pages, fin.size);
1078 }
1079 self.backend = Some(be);
1080 }
1081
1082 pub(crate) fn output_path(&self, ext: &str) -> std::path::PathBuf {
1084 let job =
1085 String::from_utf8_lossy(self.job_name.as_deref().unwrap_or(b"texput")).into_owned();
1086 let name = format!("{job}.{ext}");
1087 match &self.files.output_dir {
1088 Some(d) => d.join(&name),
1089 None => std::path::PathBuf::from(&name),
1090 }
1091 }
1092
1093 fn finish_dvi(&mut self) {
1095 let mut dvi = std::mem::take(&mut self.dvi);
1096 let pages = dvi.pages();
1097 let data = dvi.finish(self);
1098 let path = self.output_path("dvi");
1099 let name = path
1100 .file_name()
1101 .map(|n| n.to_string_lossy().into_owned())
1102 .unwrap_or_default();
1103 if std::fs::write(&path, &data).is_err() {
1104 self.printer.print_nl("! I can't write on file `");
1105 self.printer.print_bytes(name.as_bytes());
1106 self.printer.print_str("'.");
1107 return;
1108 }
1109 self.print_output_written(&name, pages, data.len() as u64);
1110 }
1111
1112 fn print_output_written(&mut self, name: &str, pages: u32, size: u64) {
1114 self.output_written = Some((name.to_owned(), pages, size));
1115 self.printer.print_nl("Output written on ");
1116 self.printer.print_bytes(name.as_bytes());
1117 self.printer.print_str(" (");
1118 self.printer.print_int(i64::from(pages));
1119 self.printer
1120 .print_str(if pages == 1 { " page" } else { " pages" });
1121 self.printer.print_str(", ");
1122 self.printer.print_int(size as i64);
1123 self.printer.print_str(" bytes).");
1124 }
1125}
1126
1127pub(crate) struct Walker {
1130 backend: Option<Box<dyn Backend>>,
1132 dvi: Option<crate::dvi::DviWriter>,
1134 h_origin: i32,
1135 v_origin: i32,
1136 page_height: i32,
1137 dvi_h: i32,
1139 dvi_v: i32,
1140 pub(crate) cur_s: i32,
1142 doing_leaders: bool,
1144 pub(crate) page: bool,
1147}
1148
1149impl Walker {
1150 pub(crate) fn pos(&self, h: i32, v: i32) -> (i32, i32) {
1153 (self.h_origin + h, self.page_height - (self.v_origin + v))
1154 }
1155}