1use crate::Engine;
26use crate::maincontrol::{IGNORE_DEPTH, Mode, ModeKind};
27use crate::nodes::{NodeData, NodeRef, Whatsit, glyph_sub, id, kern_sub};
28use crate::primitives_gen as pg;
29
30pub const DIR_NAMES: [&str; 4] = ["TLT", "TRT", "LTL", "RTT"];
32
33impl Engine {
34 pub fn print_font_id(&mut self, f: u32) {
36 let cs = self.font_id_cs(f);
37 let name = self.hash.name(cs).to_vec();
38 self.print_esc_bytes(&name);
39 }
40
41 pub fn print_font_and_char(&mut self, p: NodeRef) {
43 let (f, c) = match self.nodes.glyph(p) {
44 Some(g) => (g.font, g.chr),
45 None => return,
46 };
47 self.print_font_id(f);
48 self.printer.print_char(' ' as u32);
49 self.print_glyph_char(p, c);
50 }
51
52 fn print_glyph_char(&mut self, p: NodeRef, c: u32) {
55 match self.run_glyph_info(p) {
56 None => self.printer.print_char(c),
57 Some(Some(text)) => self.printer.print_callback_bytes(&text),
58 Some(None) => self.printer.print_hex(i64::from(c)),
60 }
61 }
62
63 pub fn short_display(&mut self, mut p: NodeRef) {
65 while !p.is_null() {
66 let n = self.nodes.get(p);
67 match &n.data {
68 NodeData::Glyph(g) => {
69 let (f, c, comps) = (g.font, g.chr, g.components);
70 if !comps.is_null() {
73 self.short_display(comps);
74 } else {
75 if i64::from(f) != self.ts.font_in_short_display {
76 self.print_font_id(f);
77 self.printer.print_char(' ' as u32);
78 self.ts.font_in_short_display = i64::from(f);
79 }
80 self.print_glyph_char(p, c);
81 }
82 }
83 NodeData::Box(_)
84 | NodeData::Ins { .. }
85 | NodeData::Whatsit(_)
86 | NodeData::Mark { .. }
87 | NodeData::Adjust { .. }
88 | NodeData::Unset { .. } => self.printer.print_str("[]"),
89 NodeData::Rule { .. } => self.printer.print_char('|' as u32),
90 NodeData::Glue { spec, .. } => {
91 if spec.width != 0 || spec.stretch != 0 || spec.shrink != 0 {
92 self.printer.print_char(' ' as u32);
93 }
94 }
95 NodeData::Math { .. } => self.printer.print_char('$' as u32),
96 NodeData::Disc { pre, post, .. } => {
97 let (pre, post) = (*pre, *post);
98 self.short_display(pre);
99 self.short_display(post);
100 }
101 _ => {}
102 }
103 p = self.nodes.next(p);
104 }
105 }
106
107 fn print_rule_dimen(&mut self, d: i32) {
108 if d == crate::typeset::NULL_FLAG {
109 self.printer.print_char('*' as u32);
110 } else {
111 self.printer.print_scaled(d);
112 }
113 }
114
115 fn node_list_display(&mut self, p: NodeRef, suffix: &[u8]) {
118 let n = self.ts.show_prefix.len();
119 self.ts.show_prefix.extend_from_slice(suffix);
120 self.show_node_list(p);
121 self.ts.show_prefix.truncate(n);
122 }
123
124 fn print_current_string(&mut self) {
126 let s = std::mem::take(&mut self.ts.show_prefix);
127 for &b in &s {
128 self.printer.print_char(u32::from(b));
129 }
130 self.ts.show_prefix = s;
131 }
132
133 pub fn show_node_list(&mut self, mut p: NodeRef) {
135 if self.ts.show_prefix.len() as i64 > i64::from(self.depth_threshold) {
136 if !p.is_null() {
137 self.printer.print_str(" []");
138 }
139 return;
140 }
141 let mut n = 0;
142 while !p.is_null() {
143 self.printer.print_ln();
144 self.print_current_string();
145 n += 1;
146 if n > self.breadth_max {
147 self.printer.print_str("etc.");
148 return;
149 }
150 self.display_node(p);
151 p = self.nodes.next(p);
152 }
153 }
154
155 pub fn display_node_list(&mut self, p: NodeRef) {
157 self.show_node_list(p);
158 }
159
160 fn display_node(&mut self, p: NodeRef) {
162 let node = self.nodes.get(p);
163 let (nid, sub) = (node.id, node.subtype);
164 match &node.data {
165 NodeData::Glyph(g) => {
166 let comps = g.components;
167 let f = g.font;
168 self.print_font_and_char(p);
169 if sub & glyph_sub::LIGATURE != 0 {
170 self.printer.print_str(" (ligature ");
171 if sub & glyph_sub::LEFT != 0 {
172 self.printer.print_char('|' as u32);
173 }
174 self.ts.font_in_short_display = i64::from(f);
175 self.short_display(comps);
176 if sub & glyph_sub::RIGHT != 0 {
177 self.printer.print_char('|' as u32);
178 }
179 self.printer.print_char(')' as u32);
180 }
181 }
182 NodeData::Box(b) => {
183 let b = (**b).clone();
184 self.print_esc_str(if nid == id::HLIST { "hbox(" } else { "vbox(" });
185 self.printer.print_scaled(b.height);
186 self.printer.print_char('+' as u32);
187 self.printer.print_scaled(b.depth);
188 self.printer.print_str(")x");
189 self.printer.print_scaled(b.width);
190 if b.glue_sign != 0 && b.glue_set != 0.0 {
191 self.printer.print_str(", glue set ");
192 if b.glue_sign == 2 {
193 self.printer.print_str("- ");
194 }
195 let g = b.glue_set;
196 if g.abs() > 20000.0 {
197 if g > 0.0 {
198 self.printer.print_char('>' as u32);
199 } else {
200 self.printer.print_str("< -");
201 }
202 self.print_glue(20000 * retex_arith::UNITY, b.glue_order, None);
203 } else {
204 self.print_glue(
205 (f64::from(retex_arith::UNITY) * g).round() as i32,
206 b.glue_order,
207 None,
208 );
209 }
210 }
211 if b.shift != 0 {
212 self.printer.print_str(", shifted ");
213 self.printer.print_scaled(b.shift);
214 }
215 self.printer.print_str(", direction ");
216 self.printer
217 .print_str(DIR_NAMES[(b.dir.rem_euclid(4)) as usize]);
218 self.node_list_display(b.head, b".");
219 }
220 NodeData::Unset {
221 b,
222 span_count,
223 stretch,
224 shrink,
225 stretch_order,
226 shrink_order,
227 } => {
228 let (h, d, w, head) = (b.height, b.depth, b.width, b.head);
229 let (sc, st, sh, sto, sho) = (
230 *span_count,
231 *stretch,
232 *shrink,
233 *stretch_order,
234 *shrink_order,
235 );
236 self.print_esc_str("unsetbox(");
237 self.printer.print_scaled(h);
238 self.printer.print_char('+' as u32);
239 self.printer.print_scaled(d);
240 self.printer.print_str(")x");
241 self.printer.print_scaled(w);
242 if sc != 0 {
243 self.printer.print_str(" (");
244 self.printer.print_int(i64::from(sc) + 1);
245 self.printer.print_str(" columns)");
246 }
247 if st != 0 {
248 self.printer.print_str(", stretch ");
249 self.print_glue(st, sto, None);
250 }
251 if sh != 0 {
252 self.printer.print_str(", shrink ");
253 self.print_glue(sh, sho, None);
254 }
255 self.node_list_display(head, b".");
256 }
257 NodeData::Rule {
258 width,
259 depth,
260 height,
261 ..
262 } => {
263 let (w, d, h) = (*width, *depth, *height);
264 self.print_esc_str(match sub {
265 crate::image::IMAGE_RULE => "image(",
266 crate::image::BOX_RESOURCE_RULE => "box(",
267 _ => "rule(",
268 });
269 self.print_rule_dimen(h);
270 self.printer.print_char('+' as u32);
271 self.print_rule_dimen(d);
272 self.printer.print_str(")x");
273 self.print_rule_dimen(w);
274 }
275 NodeData::Ins {
276 cost,
277 depth,
278 height,
279 spec,
280 head,
281 } => {
282 let (cost, depth, height, spec, head) = (*cost, *depth, *height, *spec, *head);
283 self.print_esc_str("insert");
284 self.printer.print_int(i64::from(sub));
285 self.printer.print_str(", natural size ");
286 self.printer.print_scaled(height);
287 self.printer.print_str("; split(");
288 self.print_spec(&spec, None);
289 self.printer.print_char(',' as u32);
290 self.printer.print_scaled(depth);
291 self.printer.print_str("); float cost ");
292 self.printer.print_int(i64::from(cost));
293 self.node_list_display(head, b".");
294 }
295 NodeData::Whatsit(w) => {
296 let w = (**w).clone();
297 self.display_whatsit(&w, sub);
298 }
299 NodeData::Glue { spec, leader } => {
300 let (spec, leader) = (*spec, *leader);
301 if sub >= crate::nodes::glue_sub::A_LEADERS {
302 self.print_esc_str("");
303 match sub {
304 crate::nodes::glue_sub::C_LEADERS => self.printer.print_char('c' as u32),
305 crate::nodes::glue_sub::X_LEADERS => self.printer.print_char('x' as u32),
306 crate::nodes::glue_sub::G_LEADERS => self.printer.print_char('g' as u32),
307 _ => {}
308 }
309 self.printer.print_str("leaders ");
310 self.print_spec(&spec, None);
311 self.node_list_display(leader, b".");
312 } else {
313 self.print_esc_str("glue");
314 if sub != 0 {
315 self.printer.print_char('(' as u32);
316 if sub < crate::nodes::glue_sub::COND_MATH_GLUE {
317 self.print_skip_param(sub);
318 } else if sub == crate::nodes::glue_sub::COND_MATH_GLUE {
319 self.print_esc_str("nonscript");
320 } else {
321 self.print_esc_str("mskip");
322 }
323 self.printer.print_char(')' as u32);
324 }
325 if sub != crate::nodes::glue_sub::COND_MATH_GLUE {
326 self.printer.print_char(' ' as u32);
327 if sub < crate::nodes::glue_sub::COND_MATH_GLUE {
328 self.print_spec(&spec, None);
329 } else {
330 self.print_spec(&spec, Some("mu"));
331 }
332 }
333 }
334 }
335 NodeData::Kern { kern, .. } => {
336 let k = *kern;
337 if sub == crate::nodes::glue_sub::MU_GLUE {
338 self.print_esc_str("mkern");
339 self.printer.print_scaled(k);
340 self.printer.print_str("mu");
341 } else {
342 self.print_esc_str("kern");
343 self.printer.print_scaled(k);
344 match sub {
345 kern_sub::FONT => self.printer.print_str(" (font)"),
346 kern_sub::ACCENT => self.printer.print_str(" (accent)"),
347 kern_sub::ITALIC => self.printer.print_str(" (italic)"),
348 _ => {}
349 }
350 }
351 }
352 NodeData::MarginKern { width, .. } => {
353 let w = *width;
354 self.print_esc_str("kern");
355 self.printer.print_scaled(w);
356 if sub == 0 {
357 self.printer.print_str(" (left margin)");
358 } else {
359 self.printer.print_str(" (right margin)");
360 }
361 }
362 NodeData::Math { surround, glue } => {
363 let w = *surround;
364 let g = *glue;
365 self.print_esc_str("math");
366 self.printer.print_str(if sub == 0 { "on" } else { "off" });
367 if g.width != 0 || g.stretch != 0 || g.shrink != 0 {
368 self.printer.print_str(", glued ");
369 self.print_spec(&g, None);
370 } else if w != 0 {
371 self.printer.print_str(", surrounded ");
372 self.printer.print_scaled(w);
373 }
374 }
375 NodeData::Penalty { penalty } => {
376 let v = *penalty;
377 self.print_esc_str("penalty ");
378 self.printer.print_int(i64::from(v));
379 }
380 NodeData::Disc {
381 pre,
382 post,
383 replace,
384 penalty,
385 } => {
386 let (pre, post, replace, pen) = (*pre, *post, *replace, *penalty);
387 self.print_esc_str("discretionary");
388 self.printer.print_str(" (penalty ");
389 self.printer.print_int(i64::from(pen));
390 self.printer.print_char(')' as u32);
391 self.node_list_display(pre, b".< ");
392 self.node_list_display(post, b".> ");
393 self.node_list_display(replace, b".= ");
394 }
395 NodeData::Mark { class, mark } => {
396 let (class, mark) = (*class, mark.clone());
397 self.print_esc_str("mark");
398 if class != 0 {
399 self.printer.print_char('s' as u32);
400 self.printer.print_int(i64::from(class));
401 }
402 self.printer.print_char('{' as u32);
403 let lim = self.printer.max_print_line.saturating_sub(10) as i64;
404 self.show_token_list(&mark, 0, mark.len(), lim);
405 self.printer.print_char('}' as u32);
406 }
407 NodeData::Adjust { head } => {
408 let head = *head;
409 self.print_esc_str("vadjust");
410 if sub != 0 {
411 self.printer.print_str(" pre ");
412 }
413 self.node_list_display(head, b".");
414 }
415 NodeData::Boundary { value } => {
416 let v = *value;
417 match sub {
418 0 => self.print_esc_str("noboundary"),
419 1 => {
420 self.print_esc_str("boundary=");
421 self.printer.print_int(i64::from(v));
422 }
423 2 => {
424 self.print_esc_str("protrusionboundary=");
425 self.printer.print_int(i64::from(v));
426 }
427 _ => {
428 self.print_esc_str("wordboundary=");
429 self.printer.print_int(i64::from(v));
430 }
431 }
432 }
433 NodeData::LocalPar {
434 pen_inter,
435 pen_broken,
436 box_left,
437 box_right,
438 ..
439 } => {
440 let (pi, pb, bl, br) = (*pen_inter, *pen_broken, *box_left, *box_right);
441 self.print_esc_str("localpar");
442 let base = self.ts.show_prefix.len();
443 self.ts.show_prefix.push(b'.');
444 self.printer.print_ln();
445 self.print_current_string();
446 self.print_esc_str("localinterlinepenalty=");
447 self.printer.print_int(i64::from(pi));
448 self.printer.print_ln();
449 self.print_current_string();
450 self.print_esc_str("localbrokenpenalty=");
451 self.printer.print_int(i64::from(pb));
452 for (name, b) in [("localleftbox", bl), ("localrightbox", br)] {
453 self.printer.print_ln();
454 self.print_current_string();
455 self.print_esc_str(name);
456 if b.is_null() {
457 self.printer.print_str("=null");
458 } else {
459 self.node_list_display(b, b".");
460 }
461 }
462 self.ts.show_prefix.truncate(base);
463 }
464 NodeData::Dir { dir, .. } => {
465 let d = *dir;
466 self.print_esc_str(if sub == 0 { "begindir " } else { "enddir " });
467 self.printer
468 .print_str(DIR_NAMES[(i32::from(d)).rem_euclid(4) as usize]);
469 }
470 NodeData::Temp | NodeData::Other(_) => {
471 self.printer.print_str("Unknown node type!");
472 }
473 _ => {
474 if !self.display_math_node(p) {
475 self.printer.print_str("Unknown node type!");
476 }
477 }
478 }
479 }
480
481 fn display_whatsit(&mut self, w: &Whatsit, sub: u16) {
483 match w {
484 Whatsit::Open { stream, name } => {
485 self.print_esc_str("openout");
486 self.print_write_stream(*stream as i32);
487 self.printer.print_char('=' as u32);
488 self.printer.print_bytes(name);
489 }
490 Whatsit::Write { stream, data } => {
491 self.print_esc_str("write");
492 self.print_write_stream(*stream);
493 self.printer.print_char('{' as u32);
494 let lim = self.printer.max_print_line.saturating_sub(10) as i64;
495 self.show_token_list(data, 0, data.len(), lim);
496 self.printer.print_char('}' as u32);
497 }
498 Whatsit::Close { stream } => {
499 self.print_esc_str("closeout");
500 self.print_write_stream(*stream as i32);
501 }
502 Whatsit::Special { data } => {
503 self.print_esc_str("special");
504 self.printer.print_char('{' as u32);
505 self.print_mark_bytes(data);
506 self.printer.print_char('}' as u32);
507 }
508 Whatsit::LateLua { data, .. } => {
509 self.print_esc_str("latelua0");
510 self.print_late_data(data);
511 }
512 Whatsit::PdfLiteral { data, mode, late } => {
513 self.print_esc_str(if *late {
514 "pdflateliteral "
515 } else {
516 "pdfliteral "
517 });
518 self.printer.print_str(match mode {
519 0 => "origin",
520 1 => "page",
521 2 => "direct",
522 3 => "raw",
523 4 => "text",
524 _ => "font",
525 });
526 self.print_late_data(data);
527 }
528 Whatsit::SavePos => self.print_esc_str("savepos"),
529 Whatsit::PdfColorstack { stack, cmd, data } => {
530 self.print_esc_str("pdfcolorstack ");
531 self.printer.print_int(i64::from(*stack));
532 self.printer.print_str(match cmd {
533 0 => " set ",
534 1 => " push ",
535 2 => " pop",
536 _ => " current",
537 });
538 if *cmd < 2 {
539 self.printer.print_char('{' as u32);
540 self.print_mark_bytes(data);
541 self.printer.print_char('}' as u32);
542 }
543 }
544 Whatsit::PdfSetmatrix { data } => {
545 self.print_esc_str("pdfsetmatrix");
546 self.printer.print_char('{' as u32);
547 self.print_mark_bytes(data);
548 self.printer.print_char('}' as u32);
549 }
550 Whatsit::PdfSave => self.print_esc_str("pdfsave"),
551 Whatsit::PdfRestore => self.print_esc_str("pdfrestore"),
552 Whatsit::Other { fields, data } => self.display_other_whatsit(sub, fields, data),
553 Whatsit::PdfLinkState { state } => {
554 self.print_esc_str("pdflinkstate ");
555 self.printer.print_int(i64::from(*state));
556 }
557 Whatsit::UserDefined { user_id, value } => {
558 self.print_esc_str("whatsit");
562 self.printer.print_int(i64::from(*user_id));
563 self.printer.print_char('=' as u32);
564 match value {
565 crate::nodes::UserValue::Int(n) | crate::nodes::UserValue::Lua(n) => {
566 self.printer.print_int(i64::from(*n))
567 }
568 crate::nodes::UserValue::Str(s) => {
569 self.printer.print_char('"' as u32);
570 self.printer.print_bytes(s);
571 self.printer.print_char('"' as u32);
572 }
573 crate::nodes::UserValue::Tokens(t) => {
574 self.printer.print_char('{' as u32);
575 let t = t.clone();
576 self.show_token_list(&t, 0, t.len(), i64::MAX);
577 self.printer.print_char('}' as u32);
578 }
579 crate::nodes::UserValue::Nodes(p) => {
580 self.printer.print_char('[' as u32);
581 let p = *p;
582 self.show_node_list(p);
583 self.printer.print_char(']' as u32);
584 }
585 crate::nodes::UserValue::Attr => self.printer.print_str("<>"),
586 }
587 }
588 Whatsit::PdfAnnot { dims, data, .. } => {
589 self.print_esc_str("pdfannot");
590 self.print_pdf_dims(*dims);
591 self.printer.print_char('{' as u32);
592 self.print_mark_bytes(data);
593 self.printer.print_char('}' as u32);
594 }
595 Whatsit::PdfStartLink {
596 dims, attr, action, ..
597 } => {
598 self.print_esc_str("pdfstartlink");
599 self.print_pdf_dims(*dims);
600 if !attr.is_empty() {
601 self.printer.print_str(" attr{");
602 self.print_mark_bytes(attr);
603 self.printer.print_char('}' as u32);
604 }
605 self.printer.print_str(" action ");
606 self.print_pdf_action(action);
607 }
608 Whatsit::PdfEndLink => self.print_esc_str("pdfendlink"),
609 Whatsit::PdfDest { id, view, .. } => {
611 use crate::pdfext::{DestId, DestKind};
612 self.print_esc_str("pdfdest ");
613 match id {
614 DestId::Num(n) => {
615 self.printer.print_str("num");
616 self.printer.print_int(i64::from(*n));
617 }
618 DestId::Name(n) => {
619 self.printer.print_str("name{");
620 self.print_mark_bytes(n);
621 self.printer.print_char('}' as u32);
622 }
623 }
624 self.printer.print_str(match view.kind {
625 DestKind::Xyz => " xyz",
626 DestKind::Fit => " fit",
627 DestKind::FitH => " fith",
628 DestKind::FitV => " fitv",
629 DestKind::FitB => " fitb",
630 DestKind::FitBH => " fitbh",
631 DestKind::FitBV => " fitbv",
632 DestKind::FitR => " fitr",
633 });
634 if view.kind == DestKind::Xyz && view.zoom != 0 {
635 self.printer.print_str(" zoom");
636 self.printer.print_int(i64::from(view.zoom));
637 }
638 if view.kind == DestKind::FitR {
639 self.print_pdf_dims(view.dims);
640 }
641 }
642 Whatsit::PdfRefobj { objnum } => self.show_refobj(i64::from(*objnum)),
643 }
644 }
645
646 fn show_refobj(&mut self, objnum: i64) {
651 self.print_esc_str("pdfrefobj");
652 let Some(obj) = i32::try_from(objnum)
653 .ok()
654 .and_then(|n| self.pdf.objects.get(&n))
655 .filter(|o| o.defined && !o.written)
656 .cloned()
657 else {
658 return;
659 };
660 if !obj.attr.is_empty() {
661 self.printer.print_str(" attr ");
662 self.printer.print_bytes(&obj.attr);
663 }
664 if obj.stream {
665 self.printer.print_str(" stream");
666 }
667 if obj.file {
668 self.printer.print_str(" file");
669 }
670 self.printer.print_char(' ' as u32);
671 self.printer.print_bytes(&obj.data);
672 }
673
674 fn print_pdf_dims(&mut self, d: [i32; 3]) {
677 self.printer.print_char('(' as u32);
678 self.print_rule_dimen(d[1]);
679 self.printer.print_char('+' as u32);
680 self.print_rule_dimen(d[2]);
681 self.printer.print_str(")x");
682 self.print_rule_dimen(d[0]);
683 }
684
685 fn print_pdf_action(&mut self, a: &crate::pdfext::PdfAction) {
688 use crate::pdfext::{ActionTarget, PdfAction};
689 let (file, target, goto) = match a {
690 PdfAction::User(u) => {
691 self.printer.print_str("user{");
692 self.print_mark_bytes(u);
693 self.printer.print_char('}' as u32);
694 return;
695 }
696 PdfAction::Goto {
697 file,
698 target,
699 structure,
700 ..
701 } => {
702 match structure {
704 Some(ActionTarget::Name(n)) => {
705 self.printer.print_str("struct name{");
706 self.print_mark_bytes(n);
707 self.printer.print_str("} ");
708 }
709 Some(ActionTarget::Num(n)) => {
710 self.printer.print_str("struct num");
711 self.printer.print_int(i64::from(*n));
712 self.printer.print_char(' ' as u32);
713 }
714 _ => {}
715 }
716 (file, target, true)
717 }
718 PdfAction::Thread { file, target } => (file, target, false),
719 };
720 if let Some(f) = file {
721 self.printer.print_str("file{");
722 self.print_mark_bytes(f);
723 self.printer.print_str("} ");
724 }
725 match target {
726 ActionTarget::Page(p, spec) => {
727 self.printer.print_str("page");
728 self.printer.print_int(i64::from(*p));
729 self.printer.print_char('{' as u32);
730 self.print_mark_bytes(spec);
731 self.printer.print_char('}' as u32);
732 }
733 ActionTarget::Name(n) => {
734 self.printer
735 .print_str(if goto { "goto name{" } else { "thread name{" });
736 self.print_mark_bytes(n);
737 self.printer.print_char('}' as u32);
738 }
739 ActionTarget::Num(n) => {
740 self.printer
741 .print_str(if goto { "goto num" } else { "thread num" });
742 self.printer.print_int(i64::from(*n));
743 }
744 }
745 }
746
747 fn print_mark_bytes(&mut self, data: &[u8]) {
752 let limit = self.printer.max_print_line.saturating_sub(10);
753 let mut count = 0;
754 let mut cut = None;
755 for (i, &b) in data.iter().enumerate() {
756 if b & 0xC0 != 0x80 {
759 if count == limit {
760 cut = Some(i);
761 break;
762 }
763 count += 1;
764 }
765 }
766 match cut {
767 None => self.printer.print_bytes(data),
768 Some(i) => {
769 self.printer.print_bytes(&data[..i]);
770 self.print_esc_str("ETC.");
771 }
772 }
773 }
774
775 fn display_other_whatsit(&mut self, sub: u16, fields: &[i64], data: &[u8]) {
778 use crate::nodes::whatsit_sub as ws;
779 let split = |d: &[u8]| -> (Vec<u8>, Vec<u8>) {
780 match d.iter().position(|&b| b == 0) {
781 Some(i) => (d[..i].to_vec(), d[i + 1..].to_vec()),
782 None => (d.to_vec(), Vec::new()),
783 }
784 };
785 let dims = |e: &mut Engine, f: &[i64]| {
786 if f.len() >= 3 {
787 e.printer.print_char('(' as u32);
788 e.print_rule_dimen(f[1] as i32);
789 e.printer.print_char('+' as u32);
790 e.print_rule_dimen(f[2] as i32);
791 e.printer.print_str(")x");
792 e.print_rule_dimen(f[0] as i32);
793 }
794 };
795 match sub {
796 ws::PDF_END_LINK => self.print_esc_str("pdfendlink"),
797 ws::PDF_REFOBJ => self.show_refobj(fields.first().copied().unwrap_or(0)),
798 ws::PDF_DEST => {
799 let (name, kind) = split(data);
800 self.print_esc_str("pdfdest ");
801 if !name.is_empty() && name.iter().all(u8::is_ascii_digit) {
802 self.printer.print_str("num");
803 self.printer.print_bytes(&name);
804 } else {
805 self.printer.print_str("name{");
806 self.print_mark_bytes(&name);
807 self.printer.print_char('}' as u32);
808 }
809 self.printer.print_char(' ' as u32);
810 self.printer.print_bytes(&kind);
811 }
812 ws::PDF_START_LINK => {
813 let (attr, action) = split(data);
814 self.print_esc_str("pdfstartlink");
815 dims(self, fields);
816 if !attr.is_empty() {
817 self.printer.print_str(" attr{");
818 self.print_mark_bytes(&attr);
819 self.printer.print_char('}' as u32);
820 }
821 self.printer.print_str(" action ");
822 let a = String::from_utf8_lossy(&action).into_owned();
823 if let Some(rest) = a.strip_prefix("/S /GoTo ") {
824 self.printer.print_str("goto ");
825 if let Some(n) = rest.strip_prefix("/Num ") {
826 self.printer.print_str("num");
827 self.printer.print_str(n.trim());
828 } else if let Some(n) = rest.strip_prefix("/Name (") {
829 self.printer.print_str("name{");
830 self.printer.print_str(n.trim_end_matches(')'));
831 self.printer.print_char('}' as u32);
832 } else {
833 self.printer.print_str(rest);
834 }
835 } else {
836 self.printer.print_str("user{");
837 self.print_mark_bytes(&action);
838 self.printer.print_char('}' as u32);
839 }
840 }
841 ws::PDF_ANNOT => {
842 self.print_esc_str("pdfannot");
843 dims(self, fields);
844 self.printer.print_char('{' as u32);
845 self.print_mark_bytes(data);
846 self.printer.print_char('}' as u32);
847 }
848 _ => self.printer.print_str("whatsit?"),
849 }
850 }
851
852 fn print_late_data(&mut self, data: &crate::nodes::LateData) {
853 match data {
854 crate::nodes::LateData::Tokens(t) => {
855 self.printer.print_char('{' as u32);
856 let lim = self.printer.max_print_line.saturating_sub(10) as i64;
857 self.show_token_list(t, 0, t.len(), lim);
858 self.printer.print_char('}' as u32);
859 }
860 crate::nodes::LateData::Bytes(b) => {
861 self.printer.print_char('{' as u32);
862 self.print_mark_bytes(b);
863 self.printer.print_char('}' as u32);
864 }
865 crate::nodes::LateData::Lua { reference, .. } => {
866 self.printer.print_str(" <lua data reference ");
867 self.printer.print_int(i64::from(*reference));
868 self.printer.print_char('>' as u32);
869 }
870 crate::nodes::LateData::Function { reference } => {
871 self.printer.print_str(" <function reference ");
872 self.printer.print_int(i64::from(*reference));
873 self.printer.print_char('>' as u32);
874 }
875 }
876 }
877
878 fn print_write_stream(&mut self, s: i32) {
879 if (0..16).contains(&s) {
880 self.printer.print_int(i64::from(s));
881 } else if s == 16 {
882 self.printer.print_char('*' as u32);
883 } else {
884 self.printer.print_char('-' as u32);
885 }
886 }
887
888 pub fn print_skip_param(&mut self, sub: u16) {
891 use crate::nodes::glue_sub as g;
892 let name = match sub {
893 g::LINE_SKIP => "lineskip",
894 g::BASELINE_SKIP => "baselineskip",
895 g::PAR_SKIP => "parskip",
896 g::ABOVE_DISPLAY => "abovedisplayskip",
897 g::BELOW_DISPLAY => "belowdisplayskip",
898 g::ABOVE_DISPLAY_SHORT => "abovedisplayshortskip",
899 g::BELOW_DISPLAY_SHORT => "belowdisplayshortskip",
900 g::LEFT_SKIP => "leftskip",
901 g::RIGHT_SKIP => "rightskip",
902 g::TOP_SKIP => "topskip",
903 g::SPLIT_TOP_SKIP => "splittopskip",
904 g::TAB_SKIP => "tabskip",
905 g::SPACE_SKIP => "spaceskip",
906 g::XSPACE_SKIP => "xspaceskip",
907 g::PAR_FILL_SKIP => "parfillskip",
908 g::MATH_SKIP => "mathskip",
909 g::THIN_MU_SKIP => "thinmuskip",
910 g::MED_MU_SKIP => "medmuskip",
911 g::THICK_MU_SKIP => "thickmuskip",
912 _ => {
913 self.printer.print_str("[unknown glue parameter!]");
914 return;
915 }
916 };
917 self.print_esc_str(name);
918 }
919
920 pub fn show_activities(&mut self) {
922 self.printer.print_nl("");
923 self.printer.print_ln();
924 for i in (0..self.nest.len()).rev() {
925 let (mode, line, head, aux_pd, sf, pg, lang) = {
926 let l = &self.nest[i];
927 (
928 l.mode,
929 l.mode_line,
930 l.head,
931 l.prev_depth,
932 l.space_factor,
933 l.prev_graf,
934 l.clang,
935 )
936 };
937 self.printer.print_nl("### ");
938 self.print_mode(mode);
939 self.printer.print_str(" entered at line ");
940 self.printer.print_int(i64::from(line.abs()));
941 if line < 0 {
942 self.printer.print_str(" (\\output routine)");
943 }
944 if i == 0 {
945 self.show_page_contents();
946 if !self.nodes.next(self.page.contrib_head).is_null() {
947 self.printer.print_nl("### recent contributions:");
948 }
949 }
950 let first = self.nodes.next(head);
951 self.show_box(first);
952 match mode.abs_kind() {
953 ModeKind::Vertical => {
954 self.printer.print_nl("prevdepth ");
955 if aux_pd <= IGNORE_DEPTH {
956 self.printer.print_str("ignored");
957 } else {
958 self.printer.print_scaled(aux_pd);
959 }
960 if pg != 0 {
961 self.printer.print_str(", prevgraf ");
962 self.printer.print_int(i64::from(pg));
963 self.printer
964 .print_str(if pg != 1 { " lines" } else { " line" });
965 }
966 }
967 ModeKind::Horizontal => {
968 self.printer.print_nl("spacefactor ");
969 self.printer.print_int(i64::from(sf));
970 if mode == Mode::Horizontal && lang > 0 {
971 self.printer.print_str(", current language ");
972 self.printer.print_int(i64::from(lang));
973 }
974 }
975 ModeKind::Math => {
976 let inc = self.nest[i].incompleat_noad;
977 if !inc.is_null() {
978 self.printer.print_str("this will be denominator of:");
979 self.show_box(inc);
980 }
981 }
982 ModeKind::None => {}
983 }
984 }
985 }
986
987 pub fn show_page_contents(&mut self) {
989 let head = self.page.page_head;
990 if head == self.page.page_tail {
991 return;
992 }
993 self.printer.print_nl("### current page:");
994 if self.ts.output_active {
995 self.printer.print_str(" (held over for next output)");
996 }
997 let first = self.nodes.next(head);
998 self.show_box(first);
999 if self.page.contents != crate::typeset::PageContents::Empty {
1000 self.printer.print_nl("total height ");
1001 self.print_totals();
1002 self.printer.print_nl(" goal height ");
1003 self.printer.print_scaled(self.page.so_far[0]);
1004 let ins: Vec<crate::typeset::PageIns> = self.page.ins.clone();
1005 for r in ins {
1006 self.printer.print_ln();
1007 self.print_esc_str("insert");
1008 self.printer.print_int(i64::from(r.n));
1009 self.printer.print_str(" adds ");
1010 let c = self.eqtb.count[r.n as usize].val;
1011 let t = if c == 1000 {
1012 r.height
1013 } else {
1014 retex_arith::x_over_n(r.height, 1000)
1015 .map(|d| d.quotient)
1016 .unwrap_or(0)
1017 .wrapping_mul(c)
1018 };
1019 self.printer.print_scaled(t);
1020 if r.split_up {
1021 let mut q = head;
1022 let mut t = 0;
1023 loop {
1024 q = self.nodes.next(q);
1025 if q.is_null() {
1026 break;
1027 }
1028 if self.nodes.id(q) == id::INS && u32::from(self.nodes.subtype(q)) == r.n {
1029 t += 1;
1030 }
1031 if q == r.broken_ins {
1032 break;
1033 }
1034 }
1035 self.printer.print_str(", #");
1036 self.printer.print_int(t);
1037 self.printer.print_str(" might split");
1038 }
1039 }
1040 }
1041 }
1042
1043 pub fn print_totals(&mut self) {
1045 let s = self.page.so_far;
1046 self.printer.print_scaled(s[1]);
1047 let names = ["", "fi", "fil", "fill", "filll"];
1048 for (o, nm) in names.iter().enumerate() {
1051 let v = self.page.stretch(o as u8);
1052 if v != 0 {
1053 self.printer.print_str(" plus ");
1054 self.printer.print_scaled(v);
1055 self.printer.print_str(nm);
1056 }
1057 }
1058 if s[6] != 0 {
1059 self.printer.print_str(" minus ");
1060 self.printer.print_scaled(s[6]);
1061 }
1062 }
1063
1064 pub fn show_box_param_limits(&mut self) {
1067 self.depth_threshold = self.eqtb.int(pg::assign_int::SHOWBOXDEPTH);
1068 self.breadth_max = self.eqtb.int(pg::assign_int::SHOWBOXBREADTH);
1069 if self.breadth_max <= 0 {
1070 self.breadth_max = 5;
1071 }
1072 }
1073}