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