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