1use crate::Engine;
12use crate::fonts::PacketCommand;
13use crate::glue::Glue;
14use crate::nodes::{NodeData, NodeRef, RUNNING, Whatsit, glue_sub, id};
15use crate::primitives_gen::{assign_dimen as dp, assign_int as ip};
16
17pub trait Backend {
20 fn begin_page(&mut self, engine: &Engine, width: i32, height: i32);
22 fn glyph(&mut self, engine: &Engine, font: u32, chr: u32, x: i32, y: i32);
24 fn glyph_expanded(&mut self, engine: &Engine, font: u32, chr: u32, x: i32, y: i32, ef: i32) {
27 let _ = ef;
28 self.glyph(engine, font, chr, x, y);
29 }
30 fn rule(&mut self, x: i32, y: i32, width: i32, height: i32);
32 fn literal(&mut self, mode: u8, data: &[u8], x: i32, y: i32);
34 fn print(&mut self, mode: u8, data: &[u8], x: i32, y: i32) {
37 self.literal(mode, data, x, y);
38 }
39 fn end_page(&mut self, engine: &Engine);
41 fn finish(&mut self, engine: &mut Engine) -> Option<Finished>;
43
44 fn save(&mut self, x: i32, y: i32) {
46 self.literal(0, b"q", x, y);
47 }
48
49 fn restore(&mut self, x: i32, y: i32) {
51 self.literal(0, b"Q", x, y);
52 }
53
54 fn set_matrix(&mut self, x: i32, y: i32, matrix: &[u8]) {
56 let mut d = matrix.to_vec();
57 d.extend_from_slice(b" 0 0 cm");
58 self.literal(0, &d, x, y);
59 }
60
61 fn suspend(&mut self) -> std::io::Result<()> {
64 Ok(())
65 }
66
67 fn resumable(&self) -> bool {
71 true
72 }
73
74 fn fingerprint(&mut self) -> u64 {
77 0
78 }
79
80 fn prefix(&mut self) -> Option<Vec<u8>> {
84 None
85 }
86
87 fn output_file(&self) -> Option<(std::path::PathBuf, std::path::PathBuf)> {
90 None
91 }
92
93 fn divert(&mut self, _to: &std::path::Path) -> std::io::Result<()> {
96 Err(std::io::Error::other("not supported"))
97 }
98
99 fn splice(
105 &mut self,
106 _prefix: &[u8],
107 _from: &std::path::Path,
108 _saved: &[u8],
109 _cut: u64,
110 _to: &std::path::Path,
111 ) -> std::io::Result<()> {
112 Err(std::io::Error::other("not supported"))
113 }
114
115 fn resume(&mut self) -> std::io::Result<()> {
119 Ok(())
120 }
121
122 fn abandon(&mut self, _remove: bool) {}
125
126 fn begin_object(&mut self, _engine: &Engine) {}
129
130 fn current_page_object(&self) -> Option<i32> {
132 None
133 }
134
135 fn close_page_contents(&mut self, _engine: &Engine, _items: &crate::pdfext::PageItems) {}
138
139 fn page_items(&mut self, _engine: &Engine, _items: &crate::pdfext::PageItems) {}
142
143 fn begin_box_form(
147 &mut self,
148 _engine: &Engine,
149 _index: u32,
150 _size: (i32, i32, i32),
151 _attr: &[u8],
152 _resources: &[u8],
153 ) {
154 }
155
156 fn end_box_form(&mut self, _engine: &Engine) {}
158
159 fn close_box_form_resources(&mut self, _engine: &Engine) {}
162
163 fn close_page_resources(&mut self, _engine: &Engine) {}
166
167 fn box_form(
170 &mut self,
171 _engine: &Engine,
172 _index: u32,
173 _x: i32,
174 _y: i32,
175 _width: i32,
176 _height: i32,
177 ) {
178 }
179
180 fn write_image(&mut self, _engine: &Engine, _index: u32) {}
182
183 fn image(
186 &mut self,
187 _engine: &Engine,
188 _index: u32,
189 _x: i32,
190 _y: i32,
191 _width: i32,
192 _height: i32,
193 ) {
194 }
195
196 fn written_images(&mut self) -> Option<Vec<u32>> {
200 None
201 }
202
203 fn write_object(&mut self, _engine: &Engine, _obj: &crate::pdfext::PdfObject) {}
205}
206
207#[derive(Clone, Debug, Default)]
209pub struct Finished {
210 pub file: String,
212 pub size: u64,
214 pub embedded: Vec<String>,
217 pub objstm: (u32, u32),
219}
220
221fn items_before_width(e: &Engine, head: NodeRef, q: NodeRef) -> i32 {
223 let mut w = 0;
224 let mut r = head;
225 while !r.is_null() && r != q {
226 w += e.replace_item_width(r);
227 r = e.nodes.next(r);
228 }
229 w
230}
231
232#[derive(Clone)]
234struct GlueState {
235 cur_g: i32,
236 cur_glue: f64,
237 order: u8,
238 sign: u8,
239 set: f64,
240}
241
242impl GlueState {
243 fn advance(&mut self, g: &Glue) -> i32 {
245 let mut w = g.width - self.cur_g;
246 if self.sign != 0 {
247 let applies = if self.sign == 1 {
248 g.stretch_order == self.order
249 } else {
250 g.shrink_order == self.order
251 };
252 if applies {
253 if self.sign == 1 {
254 self.cur_glue += f64::from(g.stretch);
255 } else {
256 self.cur_glue -= f64::from(g.shrink);
257 }
258 let t = (self.set * self.cur_glue).clamp(-1_000_000_000.0, 1_000_000_000.0);
259 self.cur_g = t.round() as i32;
260 }
261 }
262 w += self.cur_g;
263 w
264 }
265}
266
267impl Engine {
268 fn page_number_callback(&mut self, name: &str) -> bool {
271 let Some(lua) = self.lua.clone() else {
272 return false;
273 };
274 if !lua.has_callback(name) {
275 return false;
276 }
277 if let Err(e) = lua.call_simple(self, name) {
278 self.lua_error(&e);
279 }
280 true
281 }
282
283 pub fn ship_out(&mut self, p: NodeRef) {
285 self.work_begin(crate::profile::Work::Shipout);
286 self.ship_out_inner(p);
287 self.work_end();
288 }
289
290 fn ship_out_inner(&mut self, p: NodeRef) {
291 self.pdf.page_annots.clear();
292 let tracing = self.eqtb.int(ip::TRACINGOUTPUT) > 0;
293 if tracing {
294 self.printer.print_nl("");
295 self.printer.print_ln();
296 self.printer.print_str("Completed box being shipped out");
297 }
298 if !self.page_number_callback("start_page_number") {
301 if self.printer.term_offset > self.printer.max_print_line.saturating_sub(9) {
302 self.printer.print_ln();
303 } else if self.printer.term_offset > 0 || self.printer.file_offset > 0 {
304 self.printer.print_char(' ' as u32);
305 }
306 self.printer.print_char('[' as u32);
307 let mut j = 9;
308 while j > 0 && self.eqtb.count[j].val == 0 {
309 j -= 1;
310 }
311 for k in 0..=j {
312 self.printer.print_int(self.eqtb.count[k].val.into());
313 if k < j {
314 self.printer.print_char('.' as u32);
315 }
316 }
317 }
318 self.printer.update_terminal();
319 if p.is_null() {
320 self.luatex_warning(
322 "backend",
323 "pagedir differs from bodydir, the output may be placed wrongly on the page",
324 );
325 let msg = "no vlist or hlist in (xform) shipout";
326 if self.eqtb.int(ip::OUTPUTMODE) > 0 {
327 self.pdf_fatal(msg);
328 }
329 self.luatex_fatal("pdf backend", msg);
330 }
331 if tracing {
332 if !self.page_number_callback("stop_page_number") {
333 self.printer.print_char(']' as u32);
334 }
335 self.begin_diagnostic();
336 self.show_box(p);
337 self.end_diagnostic(true);
338 }
339 self.ship_box(p);
340 if !tracing && !self.page_number_callback("stop_page_number") {
341 self.printer.print_char(']' as u32);
342 }
343 self.dead_cycles = 0;
344 self.printer.update_terminal();
345 self.nodes.flush_list(p);
346 }
347
348 fn ship_box(&mut self, p: NodeRef) {
350 let Some(b) = self.nodes.box_data(p).cloned() else {
351 return;
352 };
353 let pdf = self.eqtb.int(ip::OUTPUTMODE) > 0;
354 let h_origin = self.eqtb.dimen(crate::backend::H_ORIGIN);
356 let v_origin = self.eqtb.dimen(crate::backend::V_ORIGIN);
357 let h_off = self.eqtb.dimen(dp::HOFFSET);
358 let v_off = self.eqtb.dimen(dp::VOFFSET);
359 let mut width = self.eqtb.dimen(dp::PAGEWIDTH);
360 let mut height = self.eqtb.dimen(dp::PAGEHEIGHT);
361 const ONE_INCH: i32 = 4_736_286;
364 if width <= 0 {
365 width = b.width + 2 * (ONE_INCH + h_off);
366 }
367 if height <= 0 {
368 height = b.height + b.depth + 2 * (ONE_INCH + v_off);
369 }
370 let mut backend = if pdf { self.backend.take() } else { None };
371 let forms_depth = self.pending_forms.len();
372 self.pending_forms.push(Vec::new());
373 if let Some(be) = backend.as_mut() {
374 self.pdf.page = Default::default();
375 be.begin_page(self, width, height);
376 if let Some(n) = be.current_page_object() {
377 let page = self.total_pages as i32 + 1;
378 self.pdf.page_refs.insert(page, n);
379 self.pdf.kinds.insert(n, crate::pdfext::ObjKind::Page);
380 }
381 let (x, y) = (h_origin + h_off, height - (v_origin + v_off + b.height));
383 for (mode, lit) in self.page_colorstack_literals() {
384 be.literal(mode, &lit, x, y);
385 }
386 }
387 let dvi = if pdf {
388 None
389 } else {
390 let mut d = std::mem::take(&mut self.dvi);
391 d.begin_page(self, (b.width, b.height, b.depth), (h_off, v_off));
392 Some(d)
393 };
394 let sync = self.synctex_recording();
395 if sync {
396 self.synctex_begin_page((h_origin + h_off, v_origin + v_off));
397 }
398 let mut walker = Walker {
399 backend,
400 dvi,
401 h_origin: h_origin + h_off,
402 v_origin: v_origin + v_off,
403 page_height: height,
404 dvi_h: h_off,
405 dvi_v: v_off,
406 cur_s: -1,
407 doing_leaders: false,
408 page: true,
409 sync,
410 matrix: None,
411 matrix_stack: Vec::new(),
412 };
413 let cur_h = if self.nodes.id(p) == id::HLIST && b.dir == 1 {
416 -b.width
417 } else {
418 0
419 };
420 let cur_v = b.height;
421 if self.nodes.id(p) == id::VLIST {
422 self.vlist_out(&mut walker, p, cur_h, cur_v);
423 } else {
424 self.hlist_out(&mut walker, p, cur_h, cur_v);
425 }
426 if let Some(mut be) = walker.backend.take() {
427 self.flush_pending_objects(be.as_mut());
428 self.collect_page_texts();
429 let items = std::mem::take(&mut self.pdf.page);
430 be.close_page_contents(self, &items);
431 self.pdf.page = items;
432 self.resolve_link_actions();
433 let items = std::mem::take(&mut self.pdf.page);
434 be.page_items(self, &items);
435 be.end_page(self);
438 let mut be = self.write_pending_forms(be);
439 be.close_page_resources(self);
440 self.flush_pending_objects(be.as_mut());
441 self.backend = Some(be);
442 let page_images = std::mem::take(&mut self.pdf.page_images);
443 let written = self.backend.as_mut().and_then(|b| b.written_images());
444 for n in written.unwrap_or(page_images) {
445 self.print_image_name(n);
446 }
447 }
448 self.pending_forms.truncate(forms_depth);
449 if let Some(mut d) = walker.dvi.take() {
450 d.end_page();
451 self.dvi = d;
452 }
453 if sync {
454 self.synctex_end_page();
455 }
456 self.total_pages += 1;
457 if let Some(f) = self.progress_hook {
458 f(self);
459 }
460 if let Some(f) = self.page_hook {
461 f(self);
462 }
463 }
464
465 pub fn finish_pdf_only(&mut self) -> bool {
469 let Some(mut be) = self.backend.take() else {
470 return false;
471 };
472 self.pdf_finish_document();
473 self.flush_pending_objects(be.as_mut());
474 let done = be.finish(self).is_some();
475 self.backend = Some(be);
476 done
477 }
478
479 fn flush_pending_objects(&mut self, be: &mut dyn Backend) {
481 for obj in std::mem::take(&mut self.pdf.pending) {
482 be.write_object(self, &obj);
483 }
484 }
485
486 pub(crate) fn print_map_file_note(&mut self, name: &str) {
489 let path = self
490 .kpse
491 .as_ref()
492 .and_then(|k| k.borrow_mut().find_file(name, retex_kpath::FileFormat::Map));
493 if let Some(path) = path {
494 let mut text = b"{".to_vec();
496 text.extend_from_slice(path.to_string_lossy().as_bytes());
497 text.push(b'}');
498 self.printer.print_lua_bytes(&text);
499 }
500 }
501
502 fn out_glyph(&mut self, w: &mut Walker, font: u32, chr: u32, h: i32, v: i32, ef: i32) {
505 self.out_glyph_at_depth(w, font, chr, h, v, ef, 0);
506 }
507
508 #[allow(clippy::too_many_arguments)]
510 fn out_glyph_at_depth(
511 &mut self,
512 w: &mut Walker,
513 font: u32,
514 chr: u32,
515 h: i32,
516 v: i32,
517 ef: i32,
518 depth: u32,
519 ) {
520 if let Some(vf) = &self.font(font).vf
521 && vf.packets.contains_key(&chr)
522 {
523 if depth < 8 {
524 let mut st = PacketState {
525 h,
526 v,
527 font: 0,
528 stack: Vec::new(),
529 ef,
530 depth,
531 vf: vf.clone(),
532 vfont: font,
533 chr,
534 };
535 let vf = vf.clone();
536 for c in &vf.packets[&chr] {
537 self.packet_command(w, &mut st, c);
538 }
539 }
540 return;
541 }
542 if !self.pdf.map_note_done
543 && w.backend.is_some()
544 && self.font(font).chars.contains_key(&chr)
545 {
546 self.pdf.map_note_done = true;
550 self.print_map_file_note("pdftex.map");
551 }
552 let (x, y) = w.pos(h, v);
553 if let Some(be) = w.backend.as_deref_mut()
555 && self.font(font).chars.contains_key(&chr)
556 {
557 if ef == 0 {
558 be.glyph(self, font, chr, x, y);
559 } else {
560 be.glyph_expanded(self, font, chr, x, y, ef);
561 }
562 }
563 if let Some(d) = w.dvi.as_mut()
564 && self.font(font).chars.contains_key(&chr)
565 {
566 let wd = crate::expansion::expanded_width(self.font_char_dimen(font, chr, 0), ef);
567 d.glyph(self, font, chr, w.dvi_h + h, w.dvi_v + v, wd);
568 }
569 }
570
571 fn packet_command(&mut self, w: &mut Walker, st: &mut PacketState, c: &PacketCommand) {
576 match c {
577 PacketCommand::Font(i) => st.font = st.vf.fonts.get(*i).copied().unwrap_or(0),
578 PacketCommand::FontId(f) => st.font = *f,
579 PacketCommand::Char(c) => {
580 if st.font == 0 {
582 st.font = st.vf.fonts.first().copied().unwrap_or(0);
583 }
584 let f = st.font;
585 if f != 0 && self.font(f).chars.contains_key(c) {
586 self.out_glyph_at_depth(w, f, *c, st.h, st.v, st.ef, st.depth + 1);
587 st.h = st.h.wrapping_add(crate::expansion::expanded_width(
588 self.font_char_dimen(f, *c, 0),
589 st.ef,
590 ));
591 }
592 }
593 PacketCommand::Rule { height, width } => {
594 if *height > 0 && *width > 0 {
595 Self::out_rule(w, st.h, st.v, *width, *height);
596 }
597 st.h = st.h.wrapping_add(*width);
598 }
599 PacketCommand::Right(x) => {
602 st.h =
603 st.h.wrapping_add(crate::expansion::expanded_width(*x, st.ef))
604 }
605 PacketCommand::Down(y) => st.v = st.v.wrapping_add(*y),
606 PacketCommand::Push => st.stack.push((st.h, st.v)),
607 PacketCommand::Pop => {
608 if let Some((h, v)) = st.stack.pop() {
609 (st.h, st.v) = (h, v);
610 }
611 }
612 PacketCommand::Special(data) => self.out_special(w, st.h, st.v, data),
613 PacketCommand::Pdf(mode, data) => {
614 let (x, y) = w.pos(st.h, st.v);
615 if let Some(be) = w.backend.as_deref_mut() {
616 be.literal(*mode, data, x, y);
617 }
618 }
619 PacketCommand::Lua(r) => self.packet_lua(w, st, *r),
620 PacketCommand::Node(p) => {
624 if matches!(self.nodes.id(*p), id::HLIST | id::VLIST) {
625 self.out_box(w, *p, st.h, st.v);
626 }
627 }
628 }
629 }
630
631 fn packet_lua(&mut self, w: &mut Walker, st: &mut PacketState, r: i32) {
636 let Some(host) = self.lua.clone() else {
637 return;
638 };
639 let walker = std::mem::take(w);
640 let saved_pos = self.pdf.pos;
641 self.pdf.pos = walker.pos(st.h, st.v);
642 let args = crate::ReferenceArgs::Glyph(st.vfont, st.chr);
643 self.vf_frames.push(VfFrame {
644 walker,
645 state: st.clone(),
646 });
647 let result = host.call_reference(self, r, args);
648 if let Some(frame) = self.vf_frames.pop() {
649 *w = frame.walker;
650 let font = st.font;
653 *st = frame.state;
654 st.font = font;
655 }
656 self.pdf.pos = saved_pos;
657 if let Err(e) = result {
658 self.lua_error(&e);
659 }
660 }
661
662 pub fn vf_command(&mut self, cmd: &PacketCommand) -> bool {
665 let Some(mut frame) = self.vf_frames.pop() else {
666 return false;
667 };
668 if matches!(cmd, PacketCommand::Char(_)) && frame.state.font == 0 {
672 self.vf_frames.push(frame);
673 return true;
674 }
675 self.packet_command(&mut frame.walker, &mut frame.state, cmd);
676 self.pdf.pos = frame.walker.pos(frame.state.h, frame.state.v);
677 self.vf_frames.push(frame);
678 true
679 }
680
681 pub fn vf_scaled(&mut self, x: i64) -> Option<i32> {
687 let f = self.vf_frames.last()?.state.vfont;
688 if !(-(1 << 24)..1 << 24).contains(&x) {
689 self.pdf_fatal_in("vf", "vf scaling");
690 }
691 let size = self.font(f).size;
692 Some(retex_font::tfm::FixScaler::new(size).scale(x as i32))
693 }
694
695 fn out_rule(w: &mut Walker, h: i32, v: i32, width: i32, height: i32) {
698 let (x, y) = w.pos(h, v);
699 if let Some(be) = w.backend.as_deref_mut() {
700 be.rule(x, y, width, height);
701 }
702 if let Some(d) = w.dvi.as_mut() {
703 d.rule(w.dvi_h + h, w.dvi_v + v, width, height);
704 }
705 }
706
707 fn out_box_resource(
711 &mut self,
712 w: &mut Walker,
713 index: i32,
714 h: i32,
715 v: i32,
716 width: i32,
717 height: i32,
718 ) {
719 let n = index as u32;
720 let (x, y) = w.pos(h, v);
721 let Some(mut be) = w.backend.take() else {
722 return;
723 };
724 if self
725 .box_resource_slot(n)
726 .is_some_and(|i| !self.box_resources[i].written)
727 {
728 match self.pending_forms.last_mut() {
729 Some(p) => {
730 if !p.contains(&n) {
731 p.push(n);
732 }
733 }
734 None => be = self.write_box_resource(be, n),
735 }
736 }
737 be.box_form(self, n, x, y, width, height);
738 w.backend = Some(be);
739 }
740
741 pub fn write_box_resource(&mut self, be: Box<dyn Backend>, n: u32) -> Box<dyn Backend> {
745 let Some(slot) = self.box_resource_slot(n) else {
746 return be;
747 };
748 let r = self.box_resources[slot].clone();
749 if r.written {
750 return be;
751 }
752 if self.eqtb.int(ip::TRACINGOUTPUT) > 0 {
756 self.printer.print_nl("");
757 self.printer.print_ln();
758 self.printer.print_str("Completed box being shipped out");
759 }
760 let mut be = be;
761 let (bw, bh, bd) = r.size;
762 self.pending_forms.push(Vec::new());
763 self.form_objects.push(Vec::new());
764 be.begin_box_form(self, n, (bw, bh, bd), &r.attr, &r.resources);
765 let sync = self.synctex_recording();
766 if sync {
767 self.synctex_begin_form(bh + bd);
768 }
769 let mut walker = Walker {
770 backend: Some(be),
771 dvi: None,
772 h_origin: 0,
773 v_origin: 0,
774 page_height: bh + bd,
775 dvi_h: 0,
776 dvi_v: 0,
777 cur_s: -1,
778 doing_leaders: false,
779 page: false,
780 sync,
781 matrix: None,
782 matrix_stack: Vec::new(),
783 };
784 if self.nodes.id(r.list) == id::VLIST {
785 self.vlist_out(&mut walker, r.list, 0, bh);
786 } else {
787 self.hlist_out(&mut walker, r.list, 0, bh);
788 }
789 let mut be = walker.backend.take().expect("the walker keeps the backend");
790 be.end_box_form(self);
791 self.box_resources[slot].written = true;
792 for obj in self.form_objects.pop().unwrap_or_default() {
793 be.write_object(self, &obj);
794 }
795 be = self.write_pending_forms(be);
796 if sync {
798 self.synctex_end_form();
799 }
800 be.close_box_form_resources(self);
801 for n in be.written_images().unwrap_or_default() {
804 self.print_image_name(n);
805 }
806 be
807 }
808
809 fn write_pending_forms(&mut self, mut be: Box<dyn Backend>) -> Box<dyn Backend> {
812 for n in self.pending_forms.pop().unwrap_or_default() {
813 be = self.write_box_resource(be, n);
814 }
815 be
816 }
817
818 fn out_image(&mut self, w: &mut Walker, index: i32, h: i32, v: i32, width: i32, height: i32) {
820 let (x, y) = w.pos(h, v);
821 if let Some(be) = w.backend.as_deref_mut() {
822 be.image(self, index as u32, x, y, width, height);
823 if self.mark_image_written(index as u32) {
824 self.pdf.page_images.push(index as u32);
825 }
826 }
827 }
828
829 fn out_box(&mut self, w: &mut Walker, p: NodeRef, h: i32, v: i32) {
832 if w.cur_s >= crate::nodes::MAX_NESTING as i32 {
835 return;
836 }
837 if self.nodes.id(p) == id::VLIST {
838 self.vlist_out(w, p, h, v);
839 } else {
840 self.hlist_out(w, p, h, v);
841 }
842 }
843
844 fn enter_box(w: &mut Walker) {
846 w.cur_s += 1;
847 if let Some(d) = w.dvi.as_mut() {
848 if w.cur_s > 0 {
849 d.push(w.cur_s);
850 } else {
851 d.note_depth(w.cur_s);
852 }
853 }
854 }
855
856 fn leave_box(w: &mut Walker) {
858 if w.cur_s > 0
859 && let Some(d) = w.dvi.as_mut()
860 {
861 d.pop();
862 }
863 w.cur_s -= 1;
864 }
865
866 fn hlist_out(&mut self, w: &mut Walker, this_box: NodeRef, mut cur_h: i32, cur_v: i32) {
869 let Some(b) = self.nodes.box_data(this_box).cloned() else {
870 return;
871 };
872 let mut gs = GlueState {
873 cur_g: 0,
874 cur_glue: 0.0,
875 order: b.glue_order,
876 sign: b.glue_sign,
877 set: b.glue_set,
878 };
879 Self::enter_box(w);
880 let base_line = cur_v;
881 let left_edge = cur_h;
882 let ctx = crate::pdfship::BoxCtx {
883 left: left_edge,
884 base: base_line,
885 width: b.width,
886 height: b.height,
887 depth: b.depth,
888 };
889 if !w.doing_leaders {
890 self.ship_continue_links(w, &ctx);
891 }
892 if w.sync {
893 let f = self.sync_of(this_box);
894 self.synctex
895 .box_open(true, f, cur_h, cur_v, [b.width, b.height, b.depth]);
896 }
897 let places = self.rtl_places(b.head, b.dir == 1, &gs);
900 let mut i = 0;
901 let mut p = b.head;
902 while !p.is_null() {
903 let next = self.nodes.next(p);
904 let rtl = match places.as_ref().and_then(|pl| pl.get(i)) {
906 Some(&(x, rtl)) => {
907 cur_h = left_edge + x;
908 i += 1;
909 rtl
910 }
911 None => false,
912 };
913 if w.sync {
914 self.sync_hlist_node(p, cur_h, base_line);
915 }
916 let n = self.nodes.get(p);
917 let n_id = n.id;
918 match &n.data {
919 NodeData::Glyph(g) => {
920 let (font, chr, xo, yo, ef) =
921 (g.font, g.chr, g.xoffset, g.yoffset, g.expansion_factor);
922 if self.fonts.char_metrics(font, chr).is_none() {
923 self.shipout_lost_char(font, chr);
924 }
925 let x = if rtl { cur_h - xo } else { cur_h + xo };
927 self.out_glyph(w, font, chr, x, base_line - yo, ef);
928 cur_h +=
929 crate::expansion::expanded_width(self.font_char_dimen(font, chr, 0), ef);
930 }
931 NodeData::Box(inner) => {
932 let (iw, shift, head) = (inner.width, inner.shift, inner.head);
933 if !head.is_null() {
934 self.out_box(w, p, cur_h, base_line + shift);
935 } else if w.sync {
936 let size = [iw, inner.height, inner.depth];
937 let f = self.sync_of(p);
938 self.synctex
939 .void_box(n_id == id::HLIST, f, cur_h, base_line, size);
940 }
941 cur_h += iw;
942 }
943 NodeData::Rule {
944 width,
945 depth,
946 height,
947 index,
948 ..
949 } => {
950 let (wd, ht, dpt, idx) = (*width, *height, *depth, *index);
951 if n.subtype == crate::image::IMAGE_RULE {
952 self.out_image(w, idx, cur_h, base_line + dpt, wd, ht + dpt);
953 } else if n.subtype == crate::image::BOX_RESOURCE_RULE {
954 self.out_box_resource(w, idx, cur_h, base_line + dpt, wd, ht + dpt);
955 } else {
956 Self::hlist_rule(w, cur_h, base_line, wd, ht, dpt, &b);
957 }
958 cur_h += wd;
959 if w.sync {
960 let f = self.sync_of(p);
961 self.synctex.rule(f, cur_h, base_line, [wd, ht, dpt]);
962 }
963 }
964 NodeData::Glue { spec, leader } => {
965 let (spec, leader, sub) = (*spec, *leader, n.subtype);
966 let wd = gs.advance(&spec);
967 let box_leaders = sub >= glue_sub::A_LEADERS
968 && !leader.is_null()
969 && self.nodes.id(leader) != id::RULE;
970 if sub >= glue_sub::A_LEADERS && !leader.is_null() {
971 self.hlist_leaders(w, leader, sub, cur_h, left_edge, base_line, wd, &b);
972 }
973 cur_h += wd;
974 if w.sync && !box_leaders {
975 let f = self.sync_of(p);
976 self.synctex.glue(f, cur_h, base_line);
977 }
978 }
979 NodeData::Kern {
980 kern,
981 expansion_factor,
982 } => {
983 let wd = kern + expansion_factor;
984 if w.sync {
985 let f = self.sync_of(p);
988 self.synctex.kern(f, *kern);
989 }
990 cur_h += wd;
991 }
992 NodeData::Math { surround, glue } => {
993 let g = *glue;
994 if w.sync {
995 let f = self.sync_of(p);
996 self.synctex.math(f, cur_h, base_line);
997 }
998 cur_h += surround;
999 if g != Glue::ZERO {
1000 cur_h += gs.advance(&g);
1001 }
1002 }
1003 NodeData::MarginKern { width, .. } => cur_h += width,
1004 NodeData::Whatsit(_) => {
1005 self.out_what(w, p, cur_h, base_line, &ctx);
1006 p = self.nodes.next(p);
1008 continue;
1009 }
1010 NodeData::Disc { replace, .. } => {
1011 let replace = *replace;
1012 let mut items = Vec::new();
1016 let mut q = replace;
1017 while !q.is_null() {
1018 items.push(q);
1019 q = self.nodes.next(q);
1020 }
1021 let start = cur_h;
1022 let total: i32 = items.iter().map(|&q| self.replace_item_width(q)).sum();
1023 for q in items {
1024 if rtl {
1025 cur_h = start + total
1027 - items_before_width(self, replace, q)
1028 - self.replace_item_width(q);
1029 }
1030 match &self.nodes.get(q).data {
1031 NodeData::Glyph(g) => {
1032 let (font, chr, ef) = (g.font, g.chr, g.expansion_factor);
1033 self.out_glyph(w, font, chr, cur_h, base_line, ef);
1034 if w.sync {
1035 self.synctex.glyph();
1036 }
1037 cur_h += crate::expansion::expanded_width(
1038 self.font_char_dimen(font, chr, 0),
1039 ef,
1040 );
1041 }
1042 NodeData::Kern {
1043 kern,
1044 expansion_factor,
1045 } => {
1046 let wd = kern + expansion_factor;
1047 if w.sync {
1048 self.synctex.end_run(cur_h, base_line);
1049 let f = self.sync_of(q);
1050 self.synctex.kern(f, *kern);
1051 }
1052 cur_h += wd;
1053 }
1054 NodeData::Box(inner) => {
1057 let (iw, shift, head) = (inner.width, inner.shift, inner.head);
1058 if !head.is_null() {
1059 self.out_box(w, q, cur_h, base_line + shift);
1060 }
1061 cur_h += iw;
1062 }
1063 NodeData::Rule {
1064 width,
1065 depth,
1066 height,
1067 ..
1068 } => {
1069 let (wd, ht, dpt) = (*width, *height, *depth);
1070 Self::hlist_rule(w, cur_h, base_line, wd, ht, dpt, &b);
1071 cur_h += wd;
1072 }
1073 _ => {}
1074 }
1075 }
1076 }
1077 _ => {}
1078 }
1079 p = next;
1080 }
1081 if places.is_some() {
1082 cur_h = left_edge + b.width;
1083 }
1084 if !w.doing_leaders {
1085 self.ship_end_box_links(w, &ctx);
1086 }
1087 if w.sync {
1088 self.synctex.end_run(cur_h, base_line);
1089 let f = self.sync_of(this_box);
1090 self.synctex.box_close(true, f, cur_h, base_line);
1091 }
1092 Self::leave_box(w);
1093 }
1094
1095 fn replace_item_width(&self, q: NodeRef) -> i32 {
1097 match &self.nodes.get(q).data {
1098 NodeData::Glyph(g) => crate::expansion::expanded_width(
1099 self.font_char_dimen(g.font, g.chr, 0),
1100 g.expansion_factor,
1101 ),
1102 NodeData::Kern {
1103 kern,
1104 expansion_factor,
1105 } => kern + expansion_factor,
1106 NodeData::Box(b) => b.width,
1107 NodeData::Rule { width, .. } => *width,
1108 _ => 0,
1109 }
1110 }
1111
1112 fn hlist_widths(&self, head: NodeRef, gs: &GlueState) -> Vec<i32> {
1115 let mut gs = gs.clone();
1116 let mut out = Vec::new();
1117 let mut p = head;
1118 while !p.is_null() {
1119 let n = self.nodes.get(p);
1120 let w = match &n.data {
1121 NodeData::Glyph(g) => crate::expansion::expanded_width(
1122 self.font_char_dimen(g.font, g.chr, 0),
1123 g.expansion_factor,
1124 ),
1125 NodeData::Box(inner) => inner.width,
1126 NodeData::Rule { width, .. } => *width,
1127 NodeData::Glue { spec, .. } => gs.advance(spec),
1128 NodeData::Kern {
1129 kern,
1130 expansion_factor,
1131 } => kern + expansion_factor,
1132 NodeData::Math { surround, glue } => {
1133 surround
1134 + if *glue != Glue::ZERO {
1135 gs.advance(glue)
1136 } else {
1137 0
1138 }
1139 }
1140 NodeData::MarginKern { width, .. } => *width,
1141 NodeData::Disc { replace, .. } => {
1142 let mut q = *replace;
1143 let mut w = 0;
1144 while !q.is_null() {
1145 w += self.replace_item_width(q);
1146 q = self.nodes.next(q);
1147 }
1148 w
1149 }
1150 _ => 0,
1151 };
1152 out.push(w);
1153 p = n.next;
1154 }
1155 out
1156 }
1157
1158 fn rtl_places(&self, head: NodeRef, box_rtl: bool, gs: &GlueState) -> Option<Vec<(i32, bool)>> {
1167 let mut has_dirs = false;
1168 let mut p = head;
1169 while !p.is_null() {
1170 if self.nodes.id(p) == id::DIR {
1171 has_dirs = true;
1172 break;
1173 }
1174 p = self.nodes.next(p);
1175 }
1176 if !box_rtl && !has_dirs {
1177 return None;
1178 }
1179 let widths = self.hlist_widths(head, gs);
1180 enum Item {
1181 Node(usize),
1182 Seg(bool, Vec<Item>),
1183 }
1184 let mut stack: Vec<(bool, Vec<Item>)> = vec![(box_rtl, Vec::new())];
1185 let close = |stack: &mut Vec<(bool, Vec<Item>)>| {
1186 let (rtl, items) = stack.pop().expect("open segment");
1187 stack
1188 .last_mut()
1189 .expect("outer segment")
1190 .1
1191 .push(Item::Seg(rtl, items));
1192 };
1193 let mut p = head;
1194 let mut k = 0;
1195 while !p.is_null() {
1196 if let NodeData::Dir { dir, .. } = self.nodes.data(p) {
1197 let begin = self.nodes.subtype(p) != crate::dirs::dir_sub::END;
1198 if !begin && stack.len() > 1 {
1199 stack.last_mut().expect("segment").1.push(Item::Node(k));
1201 close(&mut stack);
1202 } else if begin {
1203 stack.push((*dir == 1, vec![Item::Node(k)]));
1204 } else {
1205 stack.last_mut().expect("segment").1.push(Item::Node(k));
1206 }
1207 } else {
1208 stack.last_mut().expect("segment").1.push(Item::Node(k));
1209 }
1210 k += 1;
1211 p = self.nodes.next(p);
1212 }
1213 while stack.len() > 1 {
1214 close(&mut stack);
1215 }
1216 fn width(items: &[Item], widths: &[i32]) -> i32 {
1217 items
1218 .iter()
1219 .map(|it| match it {
1220 Item::Node(i) => widths[*i],
1221 Item::Seg(_, sub) => width(sub, widths),
1222 })
1223 .sum()
1224 }
1225 fn place(rtl: bool, items: &[Item], left: i32, widths: &[i32], out: &mut [(i32, bool)]) {
1226 let mut x = if rtl {
1227 left + width(items, widths)
1228 } else {
1229 left
1230 };
1231 for it in items {
1232 match it {
1233 Item::Node(i) => {
1234 if rtl {
1235 x -= widths[*i];
1236 out[*i] = (x, true);
1237 } else {
1238 out[*i] = (x, false);
1239 x += widths[*i];
1240 }
1241 }
1242 Item::Seg(r, sub) => {
1243 let w = width(sub, widths);
1244 if rtl {
1245 x -= w;
1246 place(*r, sub, x, widths, out);
1247 } else {
1248 place(*r, sub, x, widths, out);
1249 x += w;
1250 }
1251 }
1252 }
1253 }
1254 }
1255 let (rtl, items) = stack.pop().expect("box segment");
1256 let mut out = vec![(0, false); widths.len()];
1257 place(rtl, &items, 0, &widths, &mut out);
1258 Some(out)
1259 }
1260
1261 #[inline]
1264 fn sync_hlist_node(&mut self, p: NodeRef, cur_h: i32, base_line: i32) {
1265 match self.nodes.id(p) {
1266 id::GLYPH => self.synctex.glyph(),
1267 id::MARK | id::INS | id::ADJUST | id::BOUNDARY | id::LOCAL_PAR => {}
1268 _ => self.synctex.end_run(cur_h, base_line),
1269 }
1270 }
1271
1272 fn hlist_rule(
1275 w: &mut Walker,
1276 cur_h: i32,
1277 base_line: i32,
1278 wd: i32,
1279 ht: i32,
1280 dpt: i32,
1281 this_box: &crate::nodes::BoxData,
1282 ) {
1283 let ht = if ht == RUNNING { this_box.height } else { ht };
1284 let dpt = if dpt == RUNNING { this_box.depth } else { dpt };
1285 let total = ht + dpt;
1286 if total > 0 && wd > 0 {
1287 Self::out_rule(w, cur_h, base_line + dpt, wd, total);
1288 }
1289 }
1290
1291 fn vlist_out(&mut self, w: &mut Walker, this_box: NodeRef, cur_h: i32, cur_v: i32) {
1293 let Some(b) = self.nodes.box_data(this_box).cloned() else {
1294 return;
1295 };
1296 let mut gs = GlueState {
1297 cur_g: 0,
1298 cur_glue: 0.0,
1299 order: b.glue_order,
1300 sign: b.glue_sign,
1301 set: b.glue_set,
1302 };
1303 Self::enter_box(w);
1304 let left_edge = cur_h;
1305 let ctx = crate::pdfship::BoxCtx {
1306 left: left_edge,
1307 base: cur_v,
1308 width: b.width,
1309 height: b.height,
1310 depth: b.depth,
1311 };
1312 let mut cur_v = cur_v - b.height;
1313 let top_edge = cur_v;
1314 if w.sync {
1315 let f = self.sync_of(this_box);
1316 self.synctex
1317 .box_open(false, f, cur_h, cur_v, [b.width, b.height, b.depth]);
1318 }
1319 let mut p = b.head;
1320 while !p.is_null() {
1321 let next = self.nodes.next(p);
1322 let n = self.nodes.get(p);
1323 match &n.data {
1324 NodeData::Box(inner) => {
1325 let (ih, idp, shift, head) =
1326 (inner.height, inner.depth, inner.shift, inner.head);
1327 if head.is_null() {
1328 cur_v += ih + idp;
1329 if w.sync {
1330 let size = [inner.width, ih, idp];
1331 let hlist = n.id == id::HLIST;
1332 let f = self.sync_of(p);
1333 self.synctex.void_box(hlist, f, left_edge, cur_v, size);
1334 }
1335 } else {
1336 cur_v += ih;
1337 self.out_box(w, p, left_edge + shift, cur_v);
1338 cur_v += idp;
1339 }
1340 }
1341 NodeData::Rule {
1342 width,
1343 depth,
1344 height,
1345 index,
1346 ..
1347 } => {
1348 let wd = if *width == RUNNING { b.width } else { *width };
1349 let total = height + depth;
1350 let idx = *index;
1351 cur_v += total;
1352 if n.subtype == crate::image::IMAGE_RULE {
1353 self.out_image(w, idx, left_edge, cur_v, wd, total);
1354 } else if n.subtype == crate::image::BOX_RESOURCE_RULE {
1355 self.out_box_resource(w, idx, left_edge, cur_v, wd, total);
1356 } else if total > 0 && wd > 0 {
1357 Self::out_rule(w, left_edge, cur_v, wd, total);
1358 }
1359 }
1360 NodeData::Glue { spec, leader } => {
1361 let (spec, leader, sub) = (*spec, *leader, n.subtype);
1362 let ht = gs.advance(&spec);
1363 if sub >= glue_sub::A_LEADERS && !leader.is_null() {
1364 cur_v =
1365 self.vlist_leaders(w, leader, sub, cur_v, left_edge, top_edge, ht, &b);
1366 } else {
1367 cur_v += ht;
1368 }
1369 }
1370 NodeData::Kern { kern, .. } => cur_v += kern,
1371 NodeData::Whatsit(_) => {
1372 self.out_what(w, p, left_edge, cur_v, &ctx);
1373 p = self.nodes.next(p);
1375 continue;
1376 }
1377 _ => {}
1378 }
1379 p = next;
1380 }
1381 if w.sync {
1382 let f = self.sync_of(this_box);
1383 self.synctex.box_close(false, f, left_edge, cur_v);
1384 }
1385 Self::leave_box(w);
1386 }
1387
1388 fn leader_start(sub: u16, cur: i32, edge0: i32, size: i32, step: i32) -> (i32, i32) {
1392 if sub == glue_sub::C_LEADERS || sub == glue_sub::X_LEADERS {
1393 let lq = size / step;
1394 let lr = size % step;
1395 if sub == glue_sub::C_LEADERS {
1396 (cur + lr / 2, 0)
1397 } else {
1398 let lx = lr / (lq + 1);
1399 (cur + (lr - (lq - 1) * lx) / 2, lx)
1400 }
1401 } else {
1402 let mut c = edge0 + step * ((cur - edge0) / step);
1403 if c < cur {
1404 c += step;
1405 }
1406 (c, 0)
1407 }
1408 }
1409
1410 #[allow(clippy::too_many_arguments)]
1412 fn hlist_leaders(
1413 &mut self,
1414 w: &mut Walker,
1415 leader: NodeRef,
1416 sub: u16,
1417 cur_h: i32,
1418 left_edge: i32,
1419 base_line: i32,
1420 wd: i32,
1421 this_box: &crate::nodes::BoxData,
1422 ) {
1423 let lb = match &self.nodes.get(leader).data {
1424 NodeData::Rule { height, depth, .. } => {
1425 let (ht, dpt) = (*height, *depth);
1426 Self::hlist_rule(w, cur_h, base_line, wd, ht, dpt, this_box);
1427 return;
1428 }
1429 NodeData::Box(lb) => lb.clone(),
1430 _ => return,
1431 };
1432 let leader_wd = lb.width;
1433 if leader_wd <= 0 || wd <= 0 {
1434 return;
1435 }
1436 let rule_wd = wd + 10; let edge = cur_h + rule_wd;
1438 let (mut h, lx) = Self::leader_start(sub, cur_h, left_edge, rule_wd, leader_wd);
1439 while h + leader_wd <= edge {
1442 let outer = w.doing_leaders;
1443 w.doing_leaders = true;
1444 self.out_box(w, leader, h, base_line + lb.shift);
1445 w.doing_leaders = outer;
1446 h += leader_wd + lx;
1447 }
1448 }
1449
1450 #[allow(clippy::too_many_arguments)]
1453 fn vlist_leaders(
1454 &mut self,
1455 w: &mut Walker,
1456 leader: NodeRef,
1457 sub: u16,
1458 cur_v: i32,
1459 left_edge: i32,
1460 top_edge: i32,
1461 ht: i32,
1462 this_box: &crate::nodes::BoxData,
1463 ) -> i32 {
1464 let lb = match &self.nodes.get(leader).data {
1465 NodeData::Rule { width, .. } => {
1466 let wd = if *width == RUNNING {
1467 this_box.width
1468 } else {
1469 *width
1470 };
1471 if ht > 0 && wd > 0 {
1472 Self::out_rule(w, left_edge, cur_v + ht, wd, ht);
1473 }
1474 return cur_v + ht;
1475 }
1476 NodeData::Box(lb) => lb.clone(),
1477 _ => return cur_v + ht,
1478 };
1479 let leader_ht = lb.height + lb.depth;
1480 if leader_ht <= 0 || ht <= 0 {
1481 return cur_v + ht;
1482 }
1483 let rule_ht = ht + 10; let edge = cur_v + rule_ht;
1485 let (mut v, lx) = Self::leader_start(sub, cur_v, top_edge, rule_ht, leader_ht);
1486 while v + leader_ht <= edge {
1487 let outer = w.doing_leaders;
1488 w.doing_leaders = true;
1489 self.out_box(w, leader, left_edge + lb.shift, v + lb.height);
1490 w.doing_leaders = outer;
1491 v += leader_ht + lx;
1492 }
1493 edge - 10
1494 }
1495
1496 fn out_what(
1498 &mut self,
1499 w: &mut Walker,
1500 p: NodeRef,
1501 h: i32,
1502 v: i32,
1503 ctx: &crate::pdfship::BoxCtx,
1504 ) {
1505 let NodeData::Whatsit(wh) = &self.nodes.get(p).data else {
1506 return;
1507 };
1508 let (x, y) = w.pos(h, v);
1509 match (**wh).clone() {
1510 Whatsit::Open { stream, name } if !w.doing_leaders => {
1511 self.open_write_stream(stream as i32, &name)
1512 }
1513 Whatsit::Write { stream, data } if !w.doing_leaders => self.write_out(stream, &data),
1514 Whatsit::Close { stream } if !w.doing_leaders => self.close_write_stream(stream as i32),
1515 Whatsit::Special { data } => self.out_special(w, h, v, &data),
1516 Whatsit::PdfLiteral { mode, data, .. } => {
1517 let bytes = match data {
1518 crate::nodes::LateData::Bytes(b)
1519 | crate::nodes::LateData::Lua { data: b, .. } => b,
1520 crate::nodes::LateData::Function { .. } => Vec::new(),
1521 crate::nodes::LateData::Tokens(t) => {
1522 let toks = self.expand_late_tokens(&t);
1523 self.print_to_string(|e| e.show_token_list(&toks, 0, toks.len(), i64::MAX))
1524 }
1525 };
1526 if let Some(be) = w.backend.as_deref_mut() {
1527 be.literal(mode, &bytes, x, y);
1528 }
1529 }
1530 Whatsit::LateLua { data, .. } => {
1531 let code = match data {
1532 crate::nodes::LateData::Bytes(b)
1533 | crate::nodes::LateData::Lua { data: b, .. } => Ok(b),
1534 crate::nodes::LateData::Tokens(t) => {
1535 let toks = self.expand_late_tokens(&t);
1536 Ok(self.lua_code(&toks))
1537 }
1538 crate::nodes::LateData::Function { reference } => Err(reference),
1539 };
1540 self.pdf.pos = (x, y);
1543 let outer = self.pdf.late.replace(Default::default());
1544 match code {
1545 Ok(code) => self.run_lua(&code),
1546 Err(r) => {
1549 if let Some(host) = self.lua.clone()
1550 && let Err(e) =
1551 host.call_reference(self, r, crate::ReferenceArgs::Node(p))
1552 {
1553 self.lua_error(&e);
1554 }
1555 }
1556 }
1557 let ctx = std::mem::replace(&mut self.pdf.late, outer).unwrap_or_default();
1558 if let Some(be) = w.backend.as_deref_mut() {
1559 for (mode, text) in &ctx.prints {
1560 be.print(*mode, text, x, y);
1561 }
1562 }
1563 self.pdf
1564 .page
1565 .annots
1566 .extend(ctx.annots.into_iter().map(|a| (a, None)));
1567 }
1568 Whatsit::PdfRefobj { objnum } if !w.doing_leaders => self.ship_refobj(w, objnum),
1569 Whatsit::PdfAnnot { dims, objnum, data } if !w.doing_leaders => {
1570 self.ship_annot(w, ctx, h, v, dims, objnum, &data)
1571 }
1572 Whatsit::PdfStartLink {
1573 dims,
1574 objnum,
1575 attr,
1576 action,
1577 } if !w.doing_leaders => {
1578 self.ship_start_link(w, ctx, h, v, dims, objnum, &attr, &action)
1579 }
1580 Whatsit::PdfEndLink if !w.doing_leaders => self.ship_end_link(w, h, v),
1581 Whatsit::PdfDest {
1582 id,
1583 view,
1584 structure,
1585 } if !w.doing_leaders => self.ship_dest(w, ctx, h, v, &id, &view, structure),
1586 Whatsit::SavePos => {
1587 self.last_x_pos = x;
1588 self.last_y_pos = y;
1589 }
1590 Whatsit::PdfSave => {
1591 w.matrix_stack.push(w.matrix);
1592 if let Some(be) = w.backend.as_deref_mut() {
1593 be.save(x, y);
1594 }
1595 }
1596 Whatsit::PdfRestore => {
1597 if let Some(m) = w.matrix_stack.pop() {
1598 w.matrix = m;
1599 }
1600 if let Some(be) = w.backend.as_deref_mut() {
1601 be.restore(x, y);
1602 }
1603 }
1604 Whatsit::PdfSetmatrix { data } => {
1605 w.set_matrix(x, y, &data);
1606 if let Some(be) = w.backend.as_deref_mut() {
1607 be.set_matrix(x, y, &data);
1608 }
1609 }
1610 Whatsit::PdfColorstack { stack, cmd, data } => {
1611 let out = self.colorstack_op(stack, cmd, data);
1612 if let (Some((mode, lit)), Some(be)) = (out, w.backend.as_deref_mut()) {
1613 be.literal(mode, &lit, x, y);
1614 }
1615 }
1616 _ => {}
1617 }
1618 }
1619
1620 fn out_special(&mut self, w: &mut Walker, h: i32, v: i32, data: &[u8]) {
1622 let (x, y) = w.pos(h, v);
1623 if let Some(d) = w.dvi.as_mut() {
1624 d.special(w.dvi_h + h, w.dvi_v + v, data);
1625 } else if let Some(be) = w.backend.as_deref_mut() {
1626 if data.len() >= 4 && data[..4].eq_ignore_ascii_case(b"pdf:") {
1630 let rest = &data[4..];
1631 let (mode, text) = [
1632 (&b"direct:"[..], 2),
1633 (b"page:", 1),
1634 (b"raw:", 3),
1635 (b"text:", 4),
1636 (b"origin:", 0),
1637 ]
1638 .iter()
1639 .find_map(|(k, m)| rest.strip_prefix(*k).map(|t| (*m, t)))
1640 .unwrap_or((crate::pdfext::literal_mode::ORIGIN, rest));
1641 be.literal(mode, text, x, y);
1642 } else {
1643 self.printer.print_nl("Non-PDF special ignored!");
1644 }
1645 }
1646 }
1647
1648 fn colorstack_op(&mut self, stack: i32, cmd: u8, data: Vec<u8>) -> Option<(u8, Vec<u8>)> {
1650 use crate::pdfext::colorstack_cmd as c;
1651 self.pdf_ensure_colorstacks();
1652 let def = self.pdf.colorstacks.get(stack.max(0) as usize).cloned()?;
1653 let st = self
1654 .colorstack_values
1655 .entry(stack)
1656 .or_insert_with(|| vec![def.init.clone()]);
1657 match cmd {
1658 c::SET => {
1659 if let Some(top) = st.last_mut() {
1660 *top = data;
1661 } else {
1662 st.push(data);
1663 }
1664 }
1665 c::PUSH => st.push(data),
1666 c::POP => {
1667 if st.len() > 1 {
1668 st.pop();
1669 }
1670 }
1671 _ => {}
1672 }
1673 let lit = st.last().cloned().unwrap_or_default();
1674 Some((def.mode, lit))
1675 }
1676
1677 fn expand_late_tokens(&mut self, list: &crate::token::TokenList) -> Vec<crate::token::Token> {
1680 use crate::token::Token;
1681 use std::rc::Rc;
1682 self.ins_list(Rc::from(vec![
1683 Token::RIGHT_BRACE,
1684 Token::cs(self.cs.end_write),
1685 ]));
1686 self.begin_token_list(list.clone(), crate::input::TokKind::WriteText);
1687 self.ins_list(Rc::from(vec![Token::LEFT_BRACE]));
1688 let toks = self.scan_toks(false, true);
1689 self.get_token();
1690 toks
1691 }
1692
1693 pub fn finish_backend(&mut self) {
1695 if let Some(f) = self.final_hook {
1696 f(self);
1697 }
1698 if self.dvi.pages() > 0 {
1699 self.finish_dvi();
1700 } else if self.pdf.output_opened && self.eqtb.int(ip::OUTPUTMODE) <= 0 {
1701 let _ = std::fs::write(self.output_path("dvi"), b"");
1703 }
1704 let Some(mut be) = self.backend.take() else {
1705 return;
1706 };
1707 if self.pdf.fatal || self.total_pages == 0 {
1708 be.abandon(self.pdf.fatal);
1709 self.backend = Some(be);
1710 return;
1711 }
1712 if self.eqtb.int(ip::DRAFTMODE) > 0 && self.eqtb.int(ip::OUTPUTMODE) > 0 {
1713 be.abandon(true);
1718 self.backend = Some(be);
1719 self.printer
1720 .print_nl("warning (pdf backend): draftmode enabled, not changing output pdf");
1721 self.printer.print_ln();
1722 return;
1723 }
1724 if self.total_pages > 0 {
1725 self.pdf_finish_document();
1726 self.flush_pending_objects(be.as_mut());
1727 }
1728 if self.total_pages > 0
1729 && let Some(fin) = be.finish(self)
1730 {
1731 for f in &fin.embedded {
1734 self.printer.print_lua_bytes(f.as_bytes());
1735 }
1736 self.pdf.objstm_stats = fin.objstm;
1737 self.print_output_written(&fin.file, self.total_pages, fin.size);
1738 }
1739 self.backend = Some(be);
1740 }
1741
1742 pub(crate) fn output_path(&self, ext: &str) -> std::path::PathBuf {
1744 let job =
1745 String::from_utf8_lossy(self.job_name.as_deref().unwrap_or(b"texput")).into_owned();
1746 let name = format!("{job}.{ext}");
1747 match &self.files.output_dir {
1748 Some(d) => d.join(&name),
1749 None => std::path::PathBuf::from(&name),
1750 }
1751 }
1752
1753 fn finish_dvi(&mut self) {
1755 let mut dvi = std::mem::take(&mut self.dvi);
1756 let pages = dvi.pages();
1757 let data = dvi.finish(self);
1758 let path = self.output_path("dvi");
1759 let name = path
1760 .file_name()
1761 .map(|n| n.to_string_lossy().into_owned())
1762 .unwrap_or_default();
1763 if std::fs::write(&path, &data).is_err() {
1764 self.printer.print_nl("! I can't write on file `");
1765 self.printer.print_bytes(name.as_bytes());
1766 self.printer.print_str("'.");
1767 return;
1768 }
1769 self.print_output_written(&name, pages, data.len() as u64);
1770 }
1771
1772 fn print_output_written(&mut self, name: &str, pages: u32, size: u64) {
1774 self.output_written = Some((name.to_owned(), pages, size));
1775 self.printer.print_nl("Output written on ");
1776 self.printer.print_bytes(name.as_bytes());
1777 self.printer.print_str(" (");
1778 self.printer.print_int(i64::from(pages));
1779 self.printer
1780 .print_str(if pages == 1 { " page" } else { " pages" });
1781 self.printer.print_str(", ");
1782 self.printer.print_int(size as i64);
1783 self.printer.print_str(" bytes).");
1784 }
1785}
1786
1787#[derive(Default)]
1790pub(crate) struct Walker {
1791 backend: Option<Box<dyn Backend>>,
1793 dvi: Option<crate::dvi::DviWriter>,
1795 h_origin: i32,
1796 v_origin: i32,
1797 page_height: i32,
1798 dvi_h: i32,
1800 dvi_v: i32,
1801 pub(crate) cur_s: i32,
1803 doing_leaders: bool,
1805 pub(crate) page: bool,
1808 sync: bool,
1810 pub(crate) matrix: Option<[f64; 6]>,
1814 matrix_stack: Vec<Option<[f64; 6]>>,
1816}
1817
1818impl Walker {
1819 pub(crate) fn pos(&self, h: i32, v: i32) -> (i32, i32) {
1822 (self.h_origin + h, self.page_height - (self.v_origin + v))
1823 }
1824
1825 fn set_matrix(&mut self, x: i32, y: i32, data: &[u8]) {
1828 let text = String::from_utf8_lossy(data);
1829 let v: Vec<f64> = text
1830 .split_ascii_whitespace()
1831 .filter_map(|t| t.parse::<f64>().ok())
1832 .collect();
1833 let [a, b, c, d] = match v.as_slice() {
1834 [a, b, c, d, ..] => [*a, *b, *c, *d],
1835 _ => return,
1836 };
1837 let (x, y) = (f64::from(x), f64::from(y));
1838 let m = [a, b, c, d, x - a * x - c * y, y - b * x - d * y];
1840 self.matrix = Some(match self.matrix {
1841 None => m,
1842 Some(o) => [
1843 o[0] * m[0] + o[2] * m[1],
1844 o[1] * m[0] + o[3] * m[1],
1845 o[0] * m[2] + o[2] * m[3],
1846 o[1] * m[2] + o[3] * m[3],
1847 o[0] * m[4] + o[2] * m[5] + o[4],
1848 o[1] * m[4] + o[3] * m[5] + o[5],
1849 ],
1850 });
1851 }
1852}
1853
1854pub(crate) fn transform_rect(m: Option<[f64; 6]>, r: [i32; 4]) -> [i32; 4] {
1858 let Some(m) = m else { return r };
1859 let corners = [(r[0], r[1]), (r[2], r[1]), (r[0], r[3]), (r[2], r[3])];
1860 let (mut lo_x, mut lo_y, mut hi_x, mut hi_y) = (f64::MAX, f64::MAX, f64::MIN, f64::MIN);
1861 for (x, y) in corners {
1862 let (x, y) = (f64::from(x), f64::from(y));
1863 let tx = m[0] * x + m[2] * y + m[4];
1864 let ty = m[1] * x + m[3] * y + m[5];
1865 lo_x = lo_x.min(tx);
1866 lo_y = lo_y.min(ty);
1867 hi_x = hi_x.max(tx);
1868 hi_y = hi_y.max(ty);
1869 }
1870 let r = |v: f64| v.round().clamp(f64::from(i32::MIN), f64::from(i32::MAX)) as i32;
1871 [r(lo_x), r(lo_y), r(hi_x), r(hi_y)]
1872}
1873
1874#[derive(Clone)]
1876pub(crate) struct PacketState {
1877 h: i32,
1879 v: i32,
1880 font: u32,
1882 stack: Vec<(i32, i32)>,
1884 ef: i32,
1886 depth: u32,
1888 vf: std::rc::Rc<crate::fonts::VirtualFont>,
1890 vfont: u32,
1891 chr: u32,
1892}
1893
1894pub(crate) struct VfFrame {
1897 walker: Walker,
1898 state: PacketState,
1899}