1use crate::Engine;
18use crate::cmd::Cmd;
19use crate::eqtb::{Loc, Val};
20use crate::glue::{FIL, FILL, Glue};
21use crate::input::TokKind;
22use crate::maincontrol::{BoxContext, IGNORE_DEPTH, Mode, ModeKind};
23use crate::nodes::{BoxData, NodeData, NodeRef, glue_sub, id, kern_sub};
24use crate::primitives_gen::{assign_dimen as dp, assign_glue as gp, assign_int as ip};
25use crate::save::{GroupCode, SaveEntry};
26use crate::scan::ValLevel;
27use crate::token::Token;
28use crate::typeset::NULL_FLAG;
29
30#[allow(missing_docs)]
32pub mod chr {
33 pub const BOX: u32 = 0;
34 pub const COPY: u32 = 1;
35 pub const LAST_BOX: u32 = 2;
36 pub const VSPLIT: u32 = 3;
37 pub const TPACK: u32 = 4;
38 pub const VPACK: u32 = 5;
39 pub const HPACK: u32 = 6;
40 pub const VTOP: u32 = 7;
41 pub const VBOX: u32 = 8;
42 pub const HBOX: u32 = 141;
43 pub const SHIPOUT: u32 = 98;
44 pub const FIL: u32 = 0;
45 pub const FILL: u32 = 1;
46 pub const SS: u32 = 2;
47 pub const FIL_NEG: u32 = 3;
48 pub const SKIP: u32 = 4;
49 pub const MSKIP: u32 = 5;
50 pub const NOINDENT: u32 = 0;
51 pub const INDENT: u32 = 1;
52 pub const QUITVMODE: u32 = 2;
53}
54
55#[derive(Clone, Copy, Debug, PartialEq, Eq)]
57pub enum PackSpec {
58 Exactly(i32),
60 Additional(i32),
62}
63
64impl PackSpec {
65 pub const NATURAL: PackSpec = PackSpec::Additional(0);
67}
68
69const BOX_FLAG: i64 = 1 << 40;
70const GLOBAL_BOX_FLAG: i64 = BOX_FLAG + 0x1_0000;
71const SHIP_OUT_FLAG: i64 = BOX_FLAG + 0x2_0000;
72const LEADER_FLAG: i64 = BOX_FLAG + 0x3_0000;
73
74pub fn ctx_to_i64(c: BoxContext) -> i64 {
76 match c {
77 BoxContext::Shift(s) => i64::from(s),
78 BoxContext::SetBox(n) => BOX_FLAG + i64::from(n),
79 BoxContext::GlobalSetBox(n) => GLOBAL_BOX_FLAG + i64::from(n),
80 BoxContext::Shipout => SHIP_OUT_FLAG,
81 BoxContext::Leaders(s) => LEADER_FLAG + i64::from(s),
82 }
83}
84
85pub fn ctx_from_i64(v: i64) -> BoxContext {
87 if v < BOX_FLAG {
88 BoxContext::Shift(v as i32)
89 } else if v < GLOBAL_BOX_FLAG {
90 BoxContext::SetBox((v - BOX_FLAG) as u32)
91 } else if v < SHIP_OUT_FLAG {
92 BoxContext::GlobalSetBox((v - GLOBAL_BOX_FLAG) as u32)
93 } else if v == SHIP_OUT_FLAG {
94 BoxContext::Shipout
95 } else {
96 BoxContext::Leaders((v - LEADER_FLAG) as u16)
97 }
98}
99
100pub fn norm_min(h: i32) -> u8 {
102 h.clamp(1, 63) as u8
103}
104
105impl Engine {
106 pub fn push_saved(&mut self, v: i64) {
108 self.save.stack.push(SaveEntry::Value(v));
109 }
110
111 pub fn pop_saved(&mut self) -> i64 {
113 match self.save.stack.pop() {
114 Some(SaveEntry::Value(v)) => v,
115 other => {
116 if let Some(e) = other {
117 self.save.stack.push(e);
118 }
119 self.pop_saved_below()
120 }
121 }
122 }
123
124 #[cold]
131 fn pop_saved_below(&mut self) -> i64 {
132 let found = self
133 .save
134 .stack
135 .iter()
136 .rev()
137 .take_while(|e| !matches!(e, SaveEntry::Boundary { .. }))
138 .position(|e| matches!(e, SaveEntry::Value(_)));
139 match found {
140 Some(k) => {
141 let i = self.save.stack.len() - 1 - k;
142 match self.save.stack.remove(i) {
143 SaveEntry::Value(v) => v,
144 _ => 0,
145 }
146 }
147 None => {
148 debug_assert!(false, "save stack out of sync");
149 0
150 }
151 }
152 }
153
154 pub fn peek_saved(&self, k: usize) -> i64 {
156 let n = self.save.stack.len();
157 match self.save.stack.get(n.wrapping_sub(1 + k)) {
158 Some(SaveEntry::Value(v)) => *v,
159 _ => 0,
160 }
161 }
162
163 pub fn set_peek_saved(&mut self, k: usize, v: i64) {
165 let n = self.save.stack.len();
166 if let Some(SaveEntry::Value(x)) = self.save.stack.get_mut(n.wrapping_sub(1 + k)) {
167 *x = v;
168 }
169 }
170
171 pub fn new_param_glue(&mut self, sub: u16) -> NodeRef {
173 let g = self.eqtb.glue(glue_param_of_subtype(sub));
174 self.new_node(
175 id::GLUE,
176 sub,
177 NodeData::Glue {
178 spec: g,
179 leader: NodeRef::NULL,
180 },
181 )
182 }
183
184 pub fn new_glue_node(&mut self, spec: Glue, sub: u16) -> NodeRef {
186 self.new_node(
187 id::GLUE,
188 sub,
189 NodeData::Glue {
190 spec,
191 leader: NodeRef::NULL,
192 },
193 )
194 }
195
196 pub fn new_kern_node(&mut self, w: i32, sub: u16) -> NodeRef {
198 self.new_node(
199 id::KERN,
200 sub,
201 NodeData::Kern {
202 kern: w,
203 expansion_factor: 0,
204 },
205 )
206 }
207
208 pub fn new_penalty_node(&mut self, m: i32) -> NodeRef {
210 self.new_node(id::PENALTY, 0, NodeData::Penalty { penalty: m })
211 }
212
213 pub fn new_null_box_node(&mut self, hlist: bool) -> NodeRef {
215 let p = self.nodes.new_null_box(hlist);
216 self.give_new_attr(p);
217 p
218 }
219
220 pub fn typeset_command(&mut self) {
223 use ModeKind as K;
224 let kind = self.mode.abs_kind();
225 let cmd = self.cur_cmd;
226 match (kind, cmd) {
227 (
229 K::Vertical,
230 Cmd::Letter
231 | Cmd::OtherChar
232 | Cmd::CharNum
233 | Cmd::CharGiven
234 | Cmd::MathShift
235 | Cmd::MathShiftCs
236 | Cmd::UnHbox
237 | Cmd::Vrule
238 | Cmd::Accent
239 | Cmd::Discretionary
240 | Cmd::Hskip
241 | Cmd::Valign
242 | Cmd::ExSpace
243 | Cmd::Boundary
244 | Cmd::CharGhost
245 | Cmd::MathCharNum
246 | Cmd::MathGiven
247 | Cmd::XmathGiven,
248 ) => {
249 self.back_input();
250 self.new_graf(true);
251 }
252 (K::Horizontal, Cmd::MathCharNum | Cmd::MathGiven | Cmd::XmathGiven) => {
253 self.math_char_in_text();
254 }
255 (K::Vertical, Cmd::StartPar) => {
256 let c = self.cur_chr;
257 self.new_graf(c != chr::NOINDENT);
258 }
259 (K::Horizontal | K::Math, Cmd::StartPar) => self.indent_in_hmode(),
260 (K::Vertical, Cmd::Hrule) | (K::Horizontal | K::Math, Cmd::Vrule) => {
261 let r = self.scan_rule_spec();
262 self.tail_append(r);
263 if kind == K::Vertical {
264 self.cur_list_mut().prev_depth = IGNORE_DEPTH;
265 } else if kind == K::Horizontal {
266 self.cur_list_mut().space_factor = 1000;
267 }
268 }
269 (K::Vertical, Cmd::Nohrule) | (K::Horizontal | K::Math, Cmd::Novrule) => {
270 let r = self.scan_rule_spec();
271 self.nodes.set_subtype(r, 3);
272 self.tail_append(r);
273 if kind == K::Vertical {
274 self.cur_list_mut().prev_depth = IGNORE_DEPTH;
275 } else if kind == K::Horizontal {
276 self.cur_list_mut().space_factor = 1000;
277 }
278 }
279 (K::Vertical, Cmd::Vskip)
280 | (K::Horizontal | K::Math, Cmd::Hskip)
281 | (K::Math, Cmd::Mskip) => self.append_glue(),
282 (_, Cmd::Kern) | (K::Math, Cmd::Mkern) => self.append_kern(),
283 (K::Vertical, Cmd::Hmove) | (K::Horizontal | K::Math, Cmd::Vmove) => {
284 let t = self.cur_chr;
285 self.scan_normal_dimen();
286 let v = self.cur_val;
287 self.scan_box(BoxContext::Shift(if t == 0 { v } else { -v }));
288 }
289 (_, Cmd::LeaderShip) => {
290 let c = self.cur_chr;
291 if c == chr::SHIPOUT {
292 self.scan_box(BoxContext::Shipout);
293 } else if c >= glue_sub::A_LEADERS as u32 {
294 self.scan_box(BoxContext::Leaders(c as u16));
295 } else {
296 self.scan_box(BoxContext::Shipout);
298 }
299 }
300 (_, Cmd::MakeBox) => self.begin_box(BoxContext::Shift(0)),
301 (K::Vertical, Cmd::ParEnd) => {
302 self.normal_paragraph();
303 if self.mode.is_privileged() {
304 self.build_page();
305 }
306 }
307 (K::Horizontal, Cmd::ParEnd) => {
308 if self.input.align_state < 0 {
309 self.off_save();
310 }
311 self.end_graf();
312 if self.mode == Mode::Vertical {
313 self.build_page();
314 }
315 }
316 (K::Horizontal, Cmd::Stop | Cmd::Vskip | Cmd::Hrule | Cmd::UnVbox | Cmd::Halign) => {
317 self.head_for_vmode()
318 }
319 (_, Cmd::Insert) | (K::Horizontal | K::Math, Cmd::Vadjust) => {
320 self.begin_insert_or_adjust()
321 }
322 (K::Vertical, Cmd::Vadjust) => self.report_illegal_case(),
323 (_, Cmd::Mark) => self.make_mark(),
324 (_, Cmd::BreakPenalty) => self.append_penalty(),
325 (_, Cmd::RemoveItem) => self.delete_last(),
326 (K::Vertical, Cmd::UnVbox) | (K::Horizontal | K::Math, Cmd::UnHbox) => self.unpackage(),
327 (K::Horizontal, Cmd::ItalCorr) => self.append_italic_correction(),
328 (K::Math, Cmd::ItalCorr) => {
329 let k = self.new_kern_node(0, kern_sub::FONT);
330 self.tail_append(k);
331 }
332 (K::Vertical, Cmd::ItalCorr) => self.report_illegal_case(),
333 (K::Horizontal | K::Math, Cmd::Discretionary) => self.append_discretionary(),
334 (K::Horizontal, Cmd::Accent) => self.make_accent(),
335 (K::Horizontal, Cmd::Boundary) => self.append_boundary(),
336 (K::Horizontal, Cmd::CharGhost) => self.append_ghost(),
337 (_, Cmd::CarRet | Cmd::TabMark) => self.align_error(),
338 (_, Cmd::NoAlign) => self.no_align_error(),
339 (_, Cmd::Omit) => self.omit_error(),
340 (K::Vertical, Cmd::Halign) | (K::Horizontal, Cmd::Valign) => self.init_align(),
341 (K::Math, Cmd::Halign) => {
342 if self.privileged() {
343 if self.save.cur_group == GroupCode::MathShift {
344 self.init_align();
345 } else {
346 self.off_save();
347 }
348 }
349 }
350 (K::Vertical | K::Horizontal, Cmd::Endv) => self.do_endv(),
351 (_, Cmd::EndCsName) => self.cs_error(),
352 (_, Cmd::AssignLocalBox) => self.begin_local_box(),
353 (K::Horizontal, Cmd::MathShift | Cmd::MathShiftCs) => self.init_math(),
354 (K::Math, _) => self.math_command(),
355 (
356 K::Vertical | K::Horizontal,
357 Cmd::SupMark
358 | Cmd::SubMark
359 | Cmd::MathComp
360 | Cmd::DelimNum
361 | Cmd::LeftRight
362 | Cmd::Above
363 | Cmd::Radical
364 | Cmd::MathStyle
365 | Cmd::MathChoice
366 | Cmd::Vcenter
367 | Cmd::NonScript
368 | Cmd::Mkern
369 | Cmd::LimitSwitch
370 | Cmd::Mskip
371 | Cmd::MathAccent
372 | Cmd::SuperSubScript
373 | Cmd::NoSuperSubScript,
374 ) => self.insert_dollar_sign(),
375 (K::Vertical | K::Horizontal, Cmd::EqNo) => self.report_illegal_case(),
376 (_, Cmd::Option) => {
377 self.scan_int();
379 self.scan_optional_equals();
380 self.scan_int();
381 }
382 (_, Cmd::Node) => {
383 self.scan_int();
384 }
385 _ => {
386 let (c, r) = (self.cur_cmd, self.cur_chr);
387 let s = self.print_to_string(|e| e.print_cmd_chr(c, r));
388 self.not_implemented(&String::from_utf8_lossy(&s));
389 }
390 }
391 }
392
393 pub fn append_glue(&mut self) {
395 let s = self.cur_chr;
396 let g = match s {
397 chr::FIL => Glue {
398 stretch: retex_arith::UNITY,
399 stretch_order: FIL,
400 ..Glue::ZERO
401 },
402 chr::FILL => Glue {
403 stretch: retex_arith::UNITY,
404 stretch_order: FILL,
405 ..Glue::ZERO
406 },
407 chr::SS => Glue {
408 stretch: retex_arith::UNITY,
409 stretch_order: FIL,
410 shrink: retex_arith::UNITY,
411 shrink_order: FIL,
412 ..Glue::ZERO
413 },
414 chr::FIL_NEG => Glue {
415 stretch: -retex_arith::UNITY,
416 stretch_order: FIL,
417 ..Glue::ZERO
418 },
419 chr::SKIP => {
420 self.scan_glue(ValLevel::Glue);
421 self.cur_glue
422 }
423 _ => {
424 self.scan_glue(ValLevel::Mu);
425 self.cur_glue
426 }
427 };
428 let sub = if s == chr::MSKIP {
429 glue_sub::MU_GLUE
430 } else {
431 glue_sub::USER
432 };
433 let p = self.new_glue_node(g, sub);
434 self.tail_append(p);
435 }
436
437 pub fn append_kern(&mut self) {
439 let s = self.cur_chr as u16;
440 let mu = s == glue_sub::MU_GLUE;
441 self.scan_dimen(mu, false, false);
442 let v = self.cur_val;
443 let p = self.new_kern_node(
444 v,
445 if mu {
446 glue_sub::MU_GLUE
447 } else {
448 kern_sub::USER
449 },
450 );
451 self.tail_append(p);
452 }
453
454 pub fn scan_rule_spec(&mut self) -> NodeRef {
456 let vertical_rule = matches!(self.cur_cmd, Cmd::Vrule | Cmd::Novrule);
457 let (mut w, mut h, mut d) = if vertical_rule {
458 (26214, NULL_FLAG, NULL_FLAG)
459 } else {
460 (NULL_FLAG, 26214, 0)
461 };
462 let (mut left, mut right) = (0, 0);
463 let sync = self.nodes.sync.as_ref().map(|s| s.cur);
466 loop {
467 if self.scan_keyword("width") {
468 self.scan_normal_dimen();
469 w = self.cur_val;
470 continue;
471 }
472 if self.scan_keyword("height") {
473 self.scan_normal_dimen();
474 h = self.cur_val;
475 continue;
476 }
477 if self.scan_keyword("depth") {
478 self.scan_normal_dimen();
479 d = self.cur_val;
480 continue;
481 }
482 if self.scan_keyword("left") {
483 self.scan_normal_dimen();
484 left = self.cur_val;
485 continue;
486 }
487 if self.scan_keyword("right") {
488 self.scan_normal_dimen();
489 right = self.cur_val;
490 continue;
491 }
492 break;
493 }
494 let p = self.new_node(
495 id::RULE,
496 0,
497 NodeData::Rule {
498 width: w,
499 depth: d,
500 height: h,
501 dir: 0,
502 index: 0,
503 left,
504 right,
505 },
506 );
507 if let Some(f) = sync {
508 self.nodes.set_sync_fields(p, f);
509 }
510 p
511 }
512
513 pub fn scan_spec(&mut self, c: GroupCode, ctx: Option<i64>) {
516 let mut dir: i64 = -1;
520 let mut attrs: Vec<(u32, i32)> = Vec::new();
521 loop {
522 if self.scan_keyword("attr") {
523 self.scan_register_num();
524 let n = self.cur_val as u32;
525 self.scan_optional_equals();
526 self.scan_int();
527 attrs.retain(|(k, _)| *k != n);
528 attrs.push((n, self.cur_val));
529 continue;
530 }
531 if dir < 0 && self.scan_keyword("dir") {
532 dir = i64::from(self.scan_direction_spec());
533 continue;
534 }
535 if dir < 0 && self.scan_keyword("bdir") {
536 self.scan_int();
537 dir = if (0..=3).contains(&self.cur_val) {
538 i64::from(self.cur_val)
539 } else {
540 0
541 };
542 continue;
543 }
544 break;
545 }
546 let is_box = matches!(
547 c,
548 GroupCode::Hbox | GroupCode::AdjustedHbox | GroupCode::Vbox | GroupCode::Vtop
549 );
550 if dir < 0 && is_box {
551 dir = i64::from(self.default_box_dir());
552 }
553 let spec = if self.scan_keyword("to") {
554 self.scan_normal_dimen();
555 PackSpec::Exactly(self.cur_val)
556 } else if self.scan_keyword("spread") {
557 self.scan_normal_dimen();
558 PackSpec::Additional(self.cur_val)
559 } else {
560 PackSpec::NATURAL
561 };
562 if let Some(v) = ctx {
563 self.push_saved(v);
564 }
565 let (code, v) = match spec {
566 PackSpec::Exactly(v) => (0, v),
567 PackSpec::Additional(v) => (1, v),
568 };
569 self.push_saved(code);
570 self.push_saved(i64::from(v));
571 self.push_saved(dir);
572 if is_box {
573 let mut a = self.cur_attr();
576 if !attrs.is_empty() {
577 let mut v: Vec<(u32, i32)> = a.as_deref().unwrap_or(&[]).to_vec();
578 for (n, x) in attrs {
579 v.retain(|(k, _)| *k != n);
580 if x != crate::eqtb::UNUSED_ATTRIBUTE {
581 v.push((n, x));
582 }
583 }
584 v.sort_unstable();
585 a = if v.is_empty() {
586 None
587 } else {
588 Some(std::rc::Rc::from(v))
589 };
590 }
591 self.push_saved(self.ts.box_attrs.len() as i64);
592 self.ts.box_attrs.push(a);
593 }
594 self.new_save_level(c);
595 self.begin_box_dirs(c, dir as i32);
596 self.scan_left_brace();
597 }
598
599 pub fn pop_spec_pub(&mut self) -> (PackSpec, i64) {
601 self.pop_spec()
602 }
603
604 fn pop_spec(&mut self) -> (PackSpec, i64) {
606 let dir = self.pop_saved();
607 let v = self.pop_saved() as i32;
608 let code = self.pop_saved();
609 (
610 if code == 0 {
611 PackSpec::Exactly(v)
612 } else {
613 PackSpec::Additional(v)
614 },
615 dir,
616 )
617 }
618
619 pub fn scan_box(&mut self, ctx: BoxContext) {
621 self.get_nonblank_nonrelax_noncall();
622 if self.cur_cmd == Cmd::MakeBox {
623 self.begin_box(ctx);
624 } else if matches!(ctx, BoxContext::Leaders(_))
625 && matches!(
626 self.cur_cmd,
627 Cmd::Hrule | Cmd::Vrule | Cmd::Nohrule | Cmd::Novrule
628 )
629 {
630 let r = self.scan_rule_spec();
631 self.box_end(ctx, r);
632 } else {
633 self.print_err("A <box> was supposed to be here");
634 self.help(&[
635 "I was expecting to see \\hbox or \\vbox or \\copy or \\box or",
636 "something like that. So you might find something missing in",
637 "your output. But keep trying; you can fix this later.",
638 ]);
639 self.back_error();
640 }
641 }
642
643 pub fn begin_box(&mut self, ctx: BoxContext) {
646 let cur_box: NodeRef;
647 match self.cur_chr {
648 chr::BOX => {
649 self.scan_register_num();
650 let n = self.cur_val as usize;
651 cur_box = self.eqtb.boxes[n].val;
652 self.eqtb.boxes[n].val = NodeRef::NULL;
653 }
654 chr::COPY => {
655 self.scan_register_num();
656 let b = self.eqtb.boxes[self.cur_val as usize].val;
657 cur_box = self.copy_node_list(b);
658 }
659 chr::LAST_BOX => {
660 cur_box = self.take_last_box();
661 }
662 chr::VSPLIT => {
663 self.scan_register_num();
664 let n = self.cur_val as u32;
665 if !self.scan_keyword("to") {
666 self.print_err("Missing `to' inserted");
667 self.help(&[
668 "I'm working on `\\vsplit<box number> to <dimen>';",
669 "will look for the <dimen> next.",
670 ]);
671 self.error();
672 }
673 self.scan_normal_dimen();
674 let h = self.cur_val;
675 cur_box = self.vsplit(n, h);
676 }
677 c => {
678 let k = match c {
680 chr::HBOX | chr::HPACK => ModeKind::Horizontal,
681 _ => ModeKind::Vertical,
682 };
683 let ctxv = ctx_to_i64(ctx);
684 self.push_saved(i64::from(c));
686 if k == ModeKind::Horizontal {
687 if matches!(ctx, BoxContext::Shift(_))
688 && self.mode.abs_kind() == ModeKind::Vertical
689 {
690 self.scan_spec(GroupCode::AdjustedHbox, Some(ctxv));
691 } else {
692 self.scan_spec(GroupCode::Hbox, Some(ctxv));
693 }
694 } else {
695 if c == chr::VBOX || c == chr::VPACK {
696 self.scan_spec(GroupCode::Vbox, Some(ctxv));
697 } else {
698 self.scan_spec(GroupCode::Vtop, Some(ctxv));
699 }
700 self.normal_paragraph();
701 }
702 if k == ModeKind::Vertical {
703 self.push_nest(Mode::InternalVertical);
704 self.cur_list_mut().prev_depth = IGNORE_DEPTH;
705 let t = self
706 .eqtb
707 .toks_param(crate::primitives_gen::assign_toks::EVERYVBOX)
708 .clone();
709 if !t.is_empty() {
710 self.begin_token_list(t, TokKind::EveryVboxText);
711 }
712 } else {
713 self.push_nest(Mode::RestrictedHorizontal);
714 self.cur_list_mut().space_factor = 1000;
715 let t = self
716 .eqtb
717 .toks_param(crate::primitives_gen::assign_toks::EVERYHBOX)
718 .clone();
719 if !t.is_empty() {
720 self.begin_token_list(t, TokKind::EveryHboxText);
721 }
722 }
723 return;
724 }
725 }
726 self.box_end(ctx, cur_box);
727 }
728
729 pub fn copy_node_list(&mut self, p: NodeRef) -> NodeRef {
731 if p.is_null() {
732 return p;
733 }
734 self.nodes.copy_node(p)
735 }
736
737 fn take_last_box(&mut self) -> NodeRef {
739 if self.mode.abs_kind() == ModeKind::Math {
740 self.you_cant();
741 self.help(&["Sorry; this \\lastbox will be void."]);
742 self.error();
743 return NodeRef::NULL;
744 }
745 let (head, tail) = (self.cur_list().head, self.cur_list().tail);
746 if self.mode == Mode::Vertical && head == tail {
747 self.you_cant();
748 self.help(&[
749 "Sorry...I usually can't take things from the current page.",
750 "This \\lastbox will therefore be void.",
751 ]);
752 self.error();
753 return NodeRef::NULL;
754 }
755 if tail == head {
756 return NodeRef::NULL;
757 }
758 let t = self.nodes.id(tail);
759 if t != id::HLIST && t != id::VLIST {
760 return NodeRef::NULL;
761 }
762 let p = self.nodes.prev(tail);
763 let p = if p.is_null() { self.find_prev(tail) } else { p };
764 self.nodes.set_next(p, NodeRef::NULL);
765 self.nodes.set_prev(tail, NodeRef::NULL);
766 self.cur_list_mut().tail = p;
767 if let Some(b) = self.nodes.box_data_mut(tail) {
768 b.shift = 0;
769 }
770 tail
771 }
772
773 fn find_prev(&self, q: NodeRef) -> NodeRef {
775 let mut p = self.cur_list().head;
776 while !p.is_null() {
777 let n = self.nodes.next(p);
778 if n == q {
779 return p;
780 }
781 p = n;
782 }
783 self.cur_list().head
784 }
785
786 pub fn box_end(&mut self, ctx: BoxContext, cur_box: NodeRef) {
788 match ctx {
789 BoxContext::Shift(s) => {
790 if cur_box.is_null() {
791 return;
792 }
793 if let Some(b) = self.nodes.box_data_mut(cur_box) {
794 b.shift = s;
795 }
796 match self.mode.abs_kind() {
797 ModeKind::Vertical => {
798 self.append_box_with_adjustments(cur_box);
799 if self.mode.is_privileged() {
800 self.build_page();
801 }
802 }
803 ModeKind::Horizontal => {
804 self.cur_list_mut().space_factor = 1000;
805 self.tail_append(cur_box);
806 }
807 _ => {
808 self.math_box_end(cur_box);
809 }
810 }
811 }
812 BoxContext::SetBox(n) => self.set_box_register(n, false, cur_box),
813 BoxContext::GlobalSetBox(n) => self.set_box_register(n, true, cur_box),
814 BoxContext::Shipout => {
815 if !cur_box.is_null() {
816 self.ship_out(cur_box);
817 }
818 }
819 BoxContext::Leaders(sub) => {
820 if cur_box.is_null() {
821 return;
822 }
823 self.get_nonblank_nonrelax_noncall();
824 let v = self.mode.abs_kind() == ModeKind::Vertical;
825 if (self.cur_cmd == Cmd::Hskip && !v) || (self.cur_cmd == Cmd::Vskip && v) {
826 self.append_glue();
827 let t = self.cur_list().tail;
828 self.nodes.set_subtype(t, sub);
829 if let NodeData::Glue { leader, .. } = self.nodes.data_mut(t) {
830 *leader = cur_box;
831 }
832 } else {
833 self.print_err("Leaders not followed by proper glue");
834 self.help(&[
835 "You should say `\\leaders <box or rule><hskip or vskip>'.",
836 "I found the <box or rule>, but there's no suitable",
837 "<hskip or vskip>, so I'm ignoring these leaders.",
838 ]);
839 self.back_error();
840 self.nodes.flush_list(cur_box);
841 }
842 }
843 }
844 }
845
846 pub fn start_adjust_lists(&mut self) {
849 if self.ts.adjust_head.is_null() {
850 self.ts.adjust_head = self.nodes.new_temp();
851 self.ts.pre_adjust_head = self.nodes.new_temp();
852 }
853 self.nodes.set_next(self.ts.adjust_head, NodeRef::NULL);
854 self.nodes.set_next(self.ts.pre_adjust_head, NodeRef::NULL);
855 self.ts.adjust_tail = Some(self.ts.adjust_head);
856 self.ts.pre_adjust_tail = Some(self.ts.pre_adjust_head);
857 }
858
859 fn append_adjust_list(&mut self, head: NodeRef) {
861 let first = self.nodes.next(head);
862 if first.is_null() {
863 return;
864 }
865 let tail = self.cur_list().tail;
866 self.nodes.link(tail, first);
867 let t = self.nodes.tail_of(first);
868 self.cur_list_mut().tail = t;
869 self.nodes.set_next(head, NodeRef::NULL);
870 }
871
872 fn append_box_with_adjustments(&mut self, b: NodeRef) {
875 let active = self.ts.adjust_tail.is_some();
876 if active {
877 let h = self.ts.pre_adjust_head;
878 self.append_adjust_list(h);
879 }
880 self.append_to_vlist(b);
881 if active {
882 let h = self.ts.adjust_head;
883 self.append_adjust_list(h);
884 }
885 self.ts.adjust_tail = None;
886 self.ts.pre_adjust_tail = None;
887 }
888
889 pub fn set_box_register(&mut self, n: u32, global: bool, b: NodeRef) {
892 let old = self.eqtb.boxes[n as usize].val;
893 let old_level = self.eqtb.boxes[n as usize].level;
894 let saved = !global && old_level != self.save.cur_level && self.save.cur_level > 1;
895 self.define(global, Loc::Box(n), Val::Node(b));
896 if !saved && !old.is_null() && old != b {
897 self.nodes.flush_list(old);
898 }
899 }
900
901 fn package(&mut self, vtop: bool) {
903 let d = self.eqtb.dimen(dp::BOXMAXDEPTH);
904 let group = self.save.cur_group;
905 self.unsave();
906 self.end_box_dirs();
907 let ai = self.pop_saved() as usize;
908 let attr = self.ts.box_attrs.get(ai).cloned().flatten();
909 self.ts.box_attrs.truncate(ai);
910 let (spec, dir) = self.pop_spec();
911 let ctx = ctx_from_i64(self.pop_saved());
912 let kind = self.pop_saved() as u32;
913 let head = self.cur_list().head;
914 let first = self.nodes.next(head);
915 self.nodes.set_next(head, NodeRef::NULL);
916 let cur_box = if self.mode == Mode::RestrictedHorizontal {
917 let first = if kind == chr::HPACK {
918 first
919 } else {
920 let first = self.node_process_list(first, crate::nodeproc::Context::Hbox);
921 let g = if group == GroupCode::AdjustedHbox {
922 "adjusted_hbox"
923 } else {
924 "hbox"
925 };
926 self.filter_list("hpack_filter", first, g)
927 };
928 self.pack_dir = dir as i8;
929 self.hpack(first, spec)
930 } else {
931 let first = if kind == chr::VPACK || kind == chr::TPACK {
932 first
933 } else {
934 self.filter_list("vpack_filter", first, if vtop { "vtop" } else { "vbox" })
935 };
936 self.pack_dir = dir as i8;
937 let b = self.vpackage(first, spec, d);
938 if vtop {
939 let p = self.nodes.list_ptr(b);
941 let h = if !p.is_null() && self.nodes.id(p) <= id::RULE {
942 self.node_height(p)
943 } else {
944 0
945 };
946 if let Some(bd) = self.nodes.box_data_mut(b) {
947 bd.depth = bd.depth - h + bd.height;
948 bd.height = h;
949 }
950 }
951 b
952 };
953 if self.nodes.id(cur_box) == id::HLIST {
954 self.nodes.set_subtype(cur_box, 2);
955 }
956 if self.eqtb.attrs_in_use || attr.is_some() {
957 self.nodes.set_attr(cur_box, attr);
958 }
959 self.pop_nest();
960 self.box_end(ctx, cur_box);
961 }
962
963 pub fn node_height(&self, p: NodeRef) -> i32 {
965 match self.nodes.data(p) {
966 NodeData::Box(b) => b.height,
967 NodeData::Unset { b, .. } => b.height,
968 NodeData::Rule { height, .. } => *height,
969 _ => 0,
970 }
971 }
972
973 pub fn node_depth(&self, p: NodeRef) -> i32 {
975 match self.nodes.data(p) {
976 NodeData::Box(b) => b.depth,
977 NodeData::Unset { b, .. } => b.depth,
978 NodeData::Rule { depth, .. } => *depth,
979 _ => 0,
980 }
981 }
982
983 pub fn new_local_par(&mut self) -> NodeRef {
985 let lb = self.eqtb.local_box[0].val;
986 let rb = self.eqtb.local_box[1].val;
987 let box_left = self.copy_node_list(lb);
988 let box_right = self.copy_node_list(rb);
989 let box_left_width = if lb.is_null() {
990 0
991 } else {
992 self.nodes.width(lb)
993 };
994 let box_right_width = if rb.is_null() {
995 0
996 } else {
997 self.nodes.width(rb)
998 };
999 let dir = self.dir_param(crate::primitives_gen::assign_dir::PARDIR) as i8;
1000 self.new_node(
1001 id::LOCAL_PAR,
1002 0,
1003 NodeData::LocalPar {
1004 pen_inter: self.eqtb.int(ip::LOCALINTERLINEPENALTY),
1005 pen_broken: self.eqtb.int(ip::LOCALBROKENPENALTY),
1006 dir,
1007 box_left,
1008 box_left_width,
1009 box_right,
1010 box_right_width,
1011 },
1012 )
1013 }
1014
1015 pub fn new_graf(&mut self, indented: bool) {
1017 self.cur_list_mut().prev_graf = 0;
1018 let (head, tail) = (self.cur_list().head, self.cur_list().tail);
1019 if self.mode == Mode::Vertical || head != tail {
1020 let g = self.new_param_glue(glue_sub::PAR_SKIP);
1021 self.tail_append(g);
1022 }
1023 self.push_nest(Mode::Horizontal);
1024 self.cur_list_mut().space_factor = 1000;
1025 let l = self.cur_lang() as i32;
1026 self.cur_list_mut().clang = l;
1027 let lp = self.new_local_par();
1028 self.tail_append(lp);
1029 self.par_text_dir();
1030 if indented {
1031 let b = self.new_null_box_node(true);
1032 let w = self.eqtb.dimen(dp::PARINDENT);
1033 let dir = self.dir_param(crate::primitives_gen::assign_dir::PARDIR) as i8;
1034 if let Some(bd) = self.nodes.box_data_mut(b) {
1035 bd.width = w;
1036 bd.dir = dir;
1037 }
1038 self.nodes.set_subtype(b, 3);
1039 self.tail_append(b);
1040 }
1041 let t = self
1042 .eqtb
1043 .toks_param(crate::primitives_gen::assign_toks::EVERYPAR)
1044 .clone();
1045 if !t.is_empty() {
1046 self.begin_token_list(t, TokKind::EveryParText);
1047 }
1048 if self.nest.len() == 2 {
1049 self.build_page();
1050 }
1051 }
1052
1053 fn indent_in_hmode(&mut self) {
1055 if self.cur_chr == chr::INDENT {
1056 let b = self.new_null_box_node(true);
1057 let w = self.eqtb.dimen(dp::PARINDENT);
1058 if let Some(bd) = self.nodes.box_data_mut(b) {
1059 bd.width = w;
1060 }
1061 if self.mode.abs_kind() == ModeKind::Horizontal {
1062 self.cur_list_mut().space_factor = 1000;
1063 self.tail_append(b);
1064 } else {
1065 self.math_box_end(b);
1066 }
1067 }
1068 }
1069
1070 fn head_for_vmode(&mut self) {
1073 if self.mode.is_inner() {
1074 if self.cur_cmd != Cmd::Hrule {
1075 self.off_save();
1076 } else {
1077 self.print_err("You can't use `");
1078 self.print_esc_str("hrule");
1079 self.printer.print_str("' here except with leaders");
1080 self.help(&[
1081 "To put a horizontal rule in an hbox or an alignment,",
1082 "you should use \\leaders or \\hrulefill (see The TeXbook).",
1083 ]);
1084 self.error();
1085 }
1086 } else {
1087 self.back_input();
1088 self.cur_tok = Token::cs(self.par_token_cs);
1089 self.back_input();
1090 if let Some(crate::input::Level::Tokens(t)) = self.input.stack.last_mut() {
1091 t.kind = TokKind::Inserted;
1092 }
1093 }
1094 }
1095
1096 pub fn end_graf(&mut self) {
1098 if self.mode == Mode::Horizontal {
1099 let (head, tail) = (self.cur_list().head, self.cur_list().tail);
1100 let only_local_par = self.nodes.next(head) == tail
1103 && matches!(self.nodes.data(tail), NodeData::LocalPar { .. });
1104 if head == tail || only_local_par {
1105 self.nodes.flush_list(tail);
1106 self.pop_nest();
1107 } else {
1108 let wp = self.eqtb.int(ip::WIDOWPENALTY);
1109 self.line_break(wp);
1110 }
1111 self.normal_paragraph();
1112 self.err.error_count = 0;
1113 }
1114 }
1115
1116 pub fn normal_paragraph(&mut self) {
1118 use crate::primitives_gen::set_etex_shape as es;
1119 if self.eqtb.int(ip::LOOSENESS) != 0 {
1120 self.eq_define(Loc::IntPar(ip::LOOSENESS), Val::Int(0));
1121 }
1122 if self.eqtb.dimen(dp::HANGINDENT) != 0 {
1123 self.eq_define(Loc::DimenPar(dp::HANGINDENT), Val::Int(0));
1124 }
1125 if self.eqtb.int(ip::HANGAFTER) != 1 {
1126 self.eq_define(Loc::IntPar(ip::HANGAFTER), Val::Int(1));
1127 }
1128 if self.eqtb.par_shape.val.is_some() {
1129 self.eq_define(Loc::ParShape, Val::Shape(None));
1130 }
1131 if self.eqtb.etex_shape[es::INTERLINEPENALTIES as usize]
1132 .val
1133 .is_some()
1134 {
1135 self.eq_define(Loc::EtexShape(es::INTERLINEPENALTIES), Val::Shape(None));
1136 }
1137 }
1138
1139 fn begin_insert_or_adjust(&mut self) {
1141 let v: i64 = if self.cur_cmd == Cmd::Vadjust {
1142 let pre = self.scan_keyword("pre");
1143 if pre { -2 } else { -1 }
1144 } else {
1145 self.scan_register_num();
1146 let mut n = self.cur_val;
1147 if n == self.output_box_num() {
1148 self.print_err("You can't ");
1149 self.print_esc_str("insert");
1150 self.printer.print_int(i64::from(n));
1151 self.help(&["I'm changing to \\insert0; box \\outputbox is special."]);
1152 self.error();
1153 n = 0;
1154 }
1155 i64::from(n)
1156 };
1157 self.push_saved(v);
1158 self.new_save_level(GroupCode::Insert);
1159 self.scan_left_brace();
1160 self.normal_paragraph();
1161 self.push_nest(Mode::InternalVertical);
1162 self.cur_list_mut().prev_depth = IGNORE_DEPTH;
1163 }
1164
1165 pub fn output_box_num(&self) -> i32 {
1167 let n = self.eqtb.int(ip::OUTPUTBOX);
1168 if (0..65536).contains(&n) { n } else { 255 }
1169 }
1170
1171 pub fn par_token_before_group_end(&mut self, level: i32) -> bool {
1177 if self.mode != Mode::Horizontal || self.eqtb.int(ip::PARTOKENCONTEXT) < level {
1178 return false;
1179 }
1180 self.back_input();
1181 self.cur_tok = Token::cs(self.par_token_cs);
1182 self.back_input();
1183 true
1184 }
1185
1186 fn finish_insert_group(&mut self) {
1188 if self.par_token_before_group_end(2) {
1189 return;
1190 }
1191 self.end_graf();
1192 let q = self.eqtb.glue(gp::SPLITTOPSKIP);
1193 let d = self.eqtb.dimen(dp::SPLITMAXDEPTH);
1194 let f = self.eqtb.int(ip::FLOATINGPENALTY);
1195 self.unsave();
1196 let n = self.pop_saved();
1197 let head = self.cur_list().head;
1198 let first = self.nodes.next(head);
1199 self.nodes.set_next(head, NodeRef::NULL);
1200 let p = self.vpack(first, PackSpec::NATURAL);
1201 self.pop_nest();
1202 let list = self.nodes.list_ptr(p);
1203 self.nodes.set_list_ptr(p, NodeRef::NULL);
1204 let (h, dd) = match self.nodes.box_data(p) {
1205 Some(b) => (b.height, b.depth),
1206 None => (0, 0),
1207 };
1208 if n >= 0 {
1209 let node = self.new_node(
1210 id::INS,
1211 n as u16,
1212 NodeData::Ins {
1213 cost: f,
1214 depth: d,
1215 height: h + dd,
1216 spec: q,
1217 head: list,
1218 },
1219 );
1220 self.tail_append(node);
1221 } else {
1222 let node = self.new_node(
1223 id::ADJUST,
1224 if n == -2 { 1 } else { 0 },
1225 NodeData::Adjust { head: list },
1226 );
1227 self.tail_append(node);
1228 }
1229 self.nodes.free(p);
1230 if self.nest.len() == 1 {
1231 self.build_page();
1232 }
1233 }
1234
1235 fn make_mark(&mut self) {
1237 let c: u32 = if self.cur_chr == 0 {
1238 0
1239 } else if self.cur_chr == 1 {
1240 self.scan_register_num();
1242 let n = self.cur_val as u32;
1243 self.marks.classes.remove(&n);
1244 return;
1245 } else {
1246 self.scan_register_num();
1247 self.cur_val as u32
1248 };
1249 let toks = self.scan_toks_expanded();
1250 let p = self.new_node(
1251 id::MARK,
1252 0,
1253 NodeData::Mark {
1254 class: c,
1255 mark: std::rc::Rc::from(toks),
1256 },
1257 );
1258 self.tail_append(p);
1259 }
1260
1261 fn append_penalty(&mut self) {
1263 self.scan_int();
1264 let p = self.new_penalty_node(self.cur_val);
1265 self.tail_append(p);
1266 if self.mode == Mode::Vertical {
1267 self.build_page();
1268 }
1269 }
1270
1271 fn delete_last(&mut self) {
1273 let (head, tail) = (self.cur_list().head, self.cur_list().tail);
1274 let want = self.cur_chr as u8;
1275 if self.mode == Mode::Vertical && tail == head {
1276 if want != id::GLUE || self.page.last_glue.is_some() {
1278 self.you_cant();
1279 let l0 = if want == id::KERN {
1280 "Try `I\\kern-\\lastkern' instead."
1281 } else if want != id::GLUE {
1282 "Perhaps you can make the output routine do it."
1283 } else {
1284 "Try `I\\vskip-\\lastskip' instead."
1285 };
1286 self.help(&[
1287 "Sorry...I usually can't take things from the current page.",
1288 l0,
1289 ]);
1290 self.error();
1291 }
1292 return;
1293 }
1294 if tail == head || self.nodes.id(tail) != want {
1295 return;
1296 }
1297 let p = self.find_prev(tail);
1298 self.nodes.set_next(p, NodeRef::NULL);
1299 self.nodes.flush_list(tail);
1300 self.cur_list_mut().tail = p;
1301 }
1302
1303 fn unpackage(&mut self) {
1306 let c = self.cur_chr;
1307 if c > chr::COPY {
1308 let k = c as usize;
1310 let p = self.ts.disc_ptr[k];
1311 self.ts.disc_ptr[k] = NodeRef::NULL;
1312 if !p.is_null() {
1313 let tail = self.cur_list().tail;
1314 self.nodes.link(tail, p);
1315 let t = self.nodes.tail_of(p);
1316 self.cur_list_mut().tail = t;
1317 }
1318 return;
1319 }
1320 self.scan_register_num();
1321 let n = self.cur_val as usize;
1322 let p = self.eqtb.boxes[n].val;
1323 if p.is_null() {
1324 return;
1325 }
1326 let t = self.nodes.id(p);
1327 let k = self.mode.abs_kind();
1328 if k == ModeKind::Math
1329 || (k == ModeKind::Vertical && t != id::VLIST)
1330 || (k == ModeKind::Horizontal && t != id::HLIST)
1331 {
1332 self.print_err("Incompatible list can't be unboxed");
1333 self.help(&[
1334 "Sorry, Pandora. (You sneaky devil.)",
1335 "I refuse to unbox an \\hbox in vertical mode or vice versa.",
1336 "And I can't open any boxes in math mode.",
1337 ]);
1338 self.error();
1339 return;
1340 }
1341 let list = if c == chr::COPY {
1342 let l = self.nodes.list_ptr(p);
1343 self.nodes.copy_list(l)
1344 } else {
1345 let l = self.nodes.list_ptr(p);
1346 self.nodes.set_list_ptr(p, NodeRef::NULL);
1347 self.eqtb.boxes[n].val = NodeRef::NULL;
1348 self.nodes.free(p);
1349 l
1350 };
1351 let list = if t == id::HLIST {
1352 self.strip_margin_kerns(list)
1353 } else {
1354 list
1355 };
1356 if !list.is_null() {
1357 let tail = self.cur_list().tail;
1358 self.nodes.link(tail, list);
1359 let t = self.nodes.tail_of(list);
1360 self.cur_list_mut().tail = t;
1361 }
1362 }
1363
1364 fn append_italic_correction(&mut self) {
1366 let (head, tail) = (self.cur_list().head, self.cur_list().tail);
1367 if tail == head {
1368 return;
1369 }
1370 let Some(g) = self.nodes.glyph(tail) else {
1371 return;
1372 };
1373 let (f, c) = (g.font, g.chr);
1374 let ic = self.font_char_dimen(f, c, 3);
1375 let k = self.new_kern_node(ic, kern_sub::ITALIC);
1376 self.tail_append(k);
1377 }
1378
1379 fn append_discretionary(&mut self) {
1381 let c = self.cur_chr;
1382 if c == 1 {
1383 let pen = self.eqtb.int(ip::EXHYPHENPENALTY);
1385 let d = self.new_node(
1386 id::DISC,
1387 1,
1388 NodeData::Disc {
1389 pre: NodeRef::NULL,
1390 post: NodeRef::NULL,
1391 replace: NodeRef::NULL,
1392 penalty: pen,
1393 },
1394 );
1395 let lang = self.cur_lang();
1396 let (pre_c, post_c) = {
1397 let l = self.lang_params(lang);
1398 (l.pre_hyphen_char, l.post_hyphen_char)
1399 };
1400 let f = self.eqtb.cur_font.val;
1401 let pre = if pre_c > 0 {
1402 self.new_char_node(f, pre_c as u32)
1403 } else {
1404 NodeRef::NULL
1405 };
1406 let post = if post_c > 0 {
1407 self.new_char_node(f, post_c as u32)
1408 } else {
1409 NodeRef::NULL
1410 };
1411 self.nodes.set_disc_lists(d, (pre, post, NodeRef::NULL));
1412 self.tail_append(d);
1413 return;
1414 }
1415 let mut pen = self.eqtb.int(ip::HYPHENPENALTY);
1416 if self.scan_keyword("penalty") {
1417 self.scan_int();
1418 pen = self.cur_val;
1419 }
1420 let d = self.new_node(
1421 id::DISC,
1422 if c == 2 { 2 } else { 0 },
1423 NodeData::Disc {
1424 pre: NodeRef::NULL,
1425 post: NodeRef::NULL,
1426 replace: NodeRef::NULL,
1427 penalty: pen,
1428 },
1429 );
1430 self.tail_append(d);
1431 self.push_saved(0);
1432 self.new_save_level(GroupCode::Disc);
1433 self.scan_left_brace();
1434 self.push_nest(Mode::RestrictedHorizontal);
1435 self.cur_list_mut().space_factor = 1000;
1436 }
1437
1438 fn build_discretionary(&mut self) {
1441 self.unsave();
1442 let head = self.cur_list().head;
1444 let mut q = head;
1445 let mut p = self.nodes.next(q);
1446 while !p.is_null() {
1447 let t = self.nodes.id(p);
1448 if !matches!(t, id::GLYPH | id::KERN | id::HLIST | id::VLIST | id::RULE) {
1449 self.print_err("Improper discretionary list");
1450 self.help(&["Discretionary lists must contain only boxes and kerns."]);
1451 self.error();
1452 self.begin_diagnostic();
1453 self.printer
1454 .print_nl("The following discretionary sublist has been deleted:");
1455 self.show_box(p);
1456 self.end_diagnostic(true);
1457 self.nodes.flush_list(p);
1458 self.nodes.set_next(q, NodeRef::NULL);
1459 break;
1460 }
1461 q = p;
1462 p = self.nodes.next(q);
1463 }
1464 let list = self.nodes.next(head);
1465 self.nodes.set_next(head, NodeRef::NULL);
1466 if !list.is_null() {
1467 self.nodes.set_prev(list, NodeRef::NULL);
1468 }
1469 self.pop_nest();
1470 let k = self.pop_saved();
1471 let d = self.cur_list().tail;
1472 let (mut pre, mut post, mut rep) = self.nodes.disc_lists(d);
1473 match k {
1474 0 => pre = list,
1475 1 => post = list,
1476 _ => {
1477 if !list.is_null() && self.mode.abs_kind() == ModeKind::Math {
1478 self.print_err("Illegal math ");
1479 self.print_esc_str("discretionary");
1480 self.help(&[
1481 "Sorry: The third part of a discretionary break must be",
1482 "empty, in math formulas. I had to delete your third part.",
1483 ]);
1484 self.nodes.flush_list(list);
1485 self.error();
1486 } else {
1487 rep = list;
1488 }
1489 self.nodes.set_disc_lists(d, (pre, post, rep));
1490 return;
1491 }
1492 }
1493 self.nodes.set_disc_lists(d, (pre, post, rep));
1494 self.push_saved(k + 1);
1495 self.new_save_level(GroupCode::Disc);
1496 self.scan_left_brace();
1497 self.push_nest(Mode::RestrictedHorizontal);
1498 self.cur_list_mut().space_factor = 1000;
1499 }
1500
1501 fn make_accent(&mut self) {
1503 self.scan_char_num();
1504 let f = self.eqtb.cur_font.val;
1505 let p = self.new_char_node(f, self.cur_val as u32);
1506 let x = self.x_height(f);
1507 let s = f64::from(self.font(f).params.get(1).copied().unwrap_or(0)) / 65536.0;
1508 let a = self.char_width(f, self.cur_val as u32);
1509 self.do_assignments();
1510 let mut q = NodeRef::NULL;
1511 let f2 = self.eqtb.cur_font.val;
1512 if matches!(self.cur_cmd, Cmd::Letter | Cmd::OtherChar | Cmd::CharGiven) {
1513 q = self.new_char_node(f2, self.cur_chr);
1514 } else if self.cur_cmd == Cmd::CharNum {
1515 self.scan_char_num();
1516 q = self.new_char_node(f2, self.cur_val as u32);
1517 } else {
1518 self.back_input();
1519 }
1520 let mut p = p;
1521 if !q.is_null() {
1522 let t = f64::from(self.font(f2).params.get(1).copied().unwrap_or(0)) / 65536.0;
1523 let qc = self.nodes.glyph(q).map_or(0, |g| g.chr);
1524 let w = self.char_width(f2, qc);
1525 let h = self.font_char_dimen(f2, qc, 1);
1526 if h != x {
1527 p = self.hpack(p, PackSpec::NATURAL);
1528 if let Some(b) = self.nodes.box_data_mut(p) {
1529 b.shift = x - h;
1530 }
1531 }
1532 let delta =
1533 ((f64::from(w - a)) / 2.0 + f64::from(h) * t - f64::from(x) * s).round() as i32;
1534 let r = self.new_kern_node(delta, kern_sub::ACCENT);
1535 self.tail_append(r);
1536 self.tail_append(p);
1537 let k = self.new_kern_node(-a - delta, kern_sub::ACCENT);
1538 self.tail_append(k);
1539 p = q;
1540 }
1541 self.tail_append(p);
1542 self.cur_list_mut().space_factor = 1000;
1543 }
1544
1545 #[inline]
1547 pub fn char_width(&self, f: u32, c: u32) -> i32 {
1548 self.font(f).chars.get(&c).map_or(0, |m| m.width)
1549 }
1550
1551 pub fn do_assignments(&mut self) {
1553 loop {
1554 self.get_nonblank_nonrelax_noncall();
1555 if self.cur_cmd <= Cmd::MAX_NON_PREFIXED {
1556 return;
1557 }
1558 self.err.set_box_allowed = false;
1559 self.prefixed_command();
1560 self.err.set_box_allowed = true;
1561 }
1562 }
1563
1564 pub fn append_boundary(&mut self) {
1567 let c = self.cur_chr;
1568 let v = if c == 1 || c == 2 {
1569 self.scan_int();
1570 self.cur_val
1571 } else {
1572 0
1573 };
1574 let p = self.new_node(id::BOUNDARY, c as u16, NodeData::Boundary { value: v });
1575 self.tail_append(p);
1576 }
1577
1578 fn append_ghost(&mut self) {
1583 self.get_x_token();
1584 }
1585
1586 fn begin_local_box(&mut self) {
1588 let k = self.cur_chr;
1589 self.push_saved(i64::from(k));
1590 self.new_save_level(GroupCode::LocalBox);
1591 self.scan_left_brace();
1592 self.push_nest(Mode::RestrictedHorizontal);
1593 self.cur_list_mut().space_factor = 1000;
1594 }
1595
1596 fn finish_local_box(&mut self) {
1598 self.unsave();
1599 let k = self.pop_saved() as usize;
1600 let head = self.cur_list().head;
1601 let first = self.nodes.next(head);
1602 self.nodes.set_next(head, NodeRef::NULL);
1603 self.pop_nest();
1604 let b = if first.is_null() {
1605 NodeRef::NULL
1606 } else {
1607 let first = self.node_process_list(first, crate::nodeproc::Context::Hbox);
1608 let b = self.hpack(first, PackSpec::NATURAL);
1609 self.nodes.set_subtype(b, 2);
1610 b
1611 };
1612 self.eq_define(Loc::LocalBox(k as u32), Val::Node(b));
1613 if self.mode == Mode::Horizontal {
1614 let lp = self.new_local_par();
1616 self.tail_append(lp);
1617 }
1618 }
1619
1620 fn align_error(&mut self) {
1622 if self.input.align_state.abs() > 2 {
1623 self.print_err("Misplaced ");
1624 self.print_cmd_chr(self.cur_cmd, self.cur_chr);
1625 if self.cur_tok == Token::char(Cmd::TabMark, '&' as u32) {
1626 self.help(&[
1627 "I can't figure out why you would want to use a tab mark",
1628 "here. If you just want an ampersand, the remedy is",
1629 "simple: Just type `I\\&' now. But if some right brace",
1630 "up above has ended a previous alignment prematurely,",
1631 "you're probably due for more error messages, and you",
1632 "might try typing `S' now just to see what is salvageable.",
1633 ]);
1634 } else {
1635 self.help(&[
1636 "I can't figure out why you would want to use a tab mark",
1637 "or \\cr or \\span just now. If something like a right brace",
1638 "up above has ended a previous alignment prematurely,",
1639 "you're probably due for more error messages, and you",
1640 "might try typing `S' now just to see what is salvageable.",
1641 ]);
1642 }
1643 self.error();
1644 } else {
1645 self.back_input();
1646 if self.input.align_state < 0 {
1647 self.print_err("Missing { inserted");
1648 self.input.align_state += 1;
1649 self.cur_tok = Token::LEFT_BRACE;
1650 } else {
1651 self.print_err("Missing } inserted");
1652 self.input.align_state -= 1;
1653 self.cur_tok = Token::RIGHT_BRACE;
1654 }
1655 self.help(&[
1656 "I've put in what seems to be necessary to fix",
1657 "the current column of the current alignment.",
1658 "Try to go on, since this might almost work.",
1659 ]);
1660 self.ins_error();
1661 }
1662 }
1663
1664 fn no_align_error(&mut self) {
1665 self.print_err("Misplaced ");
1666 self.print_esc_str("noalign");
1667 self.help(&[
1668 "I expect to see \\noalign only after the \\cr of",
1669 "an alignment. Proceed, and I'll ignore this case.",
1670 ]);
1671 self.error();
1672 }
1673
1674 fn omit_error(&mut self) {
1675 self.print_err("Misplaced ");
1676 self.print_esc_str("omit");
1677 self.help(&[
1678 "I expect to see \\omit only after tab marks or the \\cr of",
1679 "an alignment. Proceed, and I'll ignore this case.",
1680 ]);
1681 self.error();
1682 }
1683
1684 fn cs_error(&mut self) {
1685 self.print_err("Extra ");
1686 self.print_esc_str("endcsname");
1687 self.help(&["I'm ignoring this, since I wasn't doing a \\csname."]);
1688 self.error();
1689 }
1690
1691 pub fn append_to_vlist(&mut self, b: NodeRef) {
1694 let pd = self.cur_list().prev_depth;
1695 if pd > IGNORE_DEPTH {
1696 let bs = self.eqtb.glue(gp::BASELINESKIP);
1697 let h = self.node_height(b);
1698 let d = bs.width - pd - h;
1699 let p = if d < self.eqtb.dimen(dp::LINESKIPLIMIT) {
1700 self.new_param_glue(glue_sub::LINE_SKIP)
1701 } else {
1702 let g = Glue { width: d, ..bs };
1703 self.new_glue_node(g, glue_sub::BASELINE_SKIP)
1704 };
1705 self.tail_append(p);
1706 }
1707 self.tail_append(b);
1708 let d = self.node_depth(b);
1709 self.cur_list_mut().prev_depth = d;
1710 }
1711
1712 pub fn handle_right_brace_typesetting(&mut self) {
1715 match self.save.cur_group {
1716 GroupCode::Hbox => self.package(false),
1717 GroupCode::AdjustedHbox => {
1718 self.start_adjust_lists();
1719 self.package(false);
1720 }
1721 GroupCode::Vbox => {
1722 if self.par_token_before_group_end(1) {
1723 return;
1724 }
1725 self.end_graf();
1726 self.package(false);
1727 }
1728 GroupCode::Vtop => {
1729 if self.par_token_before_group_end(1) {
1730 return;
1731 }
1732 self.end_graf();
1733 self.package(true);
1734 }
1735 GroupCode::Insert => self.finish_insert_group(),
1736 GroupCode::Output => self.resume_output(),
1737 GroupCode::Disc => self.build_discretionary(),
1738 GroupCode::Align => {
1739 self.back_input();
1740 self.cur_tok = Token::cs(self.cs.frozen_cr);
1741 self.print_err("Missing ");
1742 self.print_esc_str("cr");
1743 self.printer.print_str(" inserted");
1744 self.help(&["I'm guessing that you meant to end an alignment here."]);
1745 self.ins_error();
1746 }
1747 GroupCode::NoAlign => {
1748 if self.par_token_before_group_end(2) {
1749 return;
1750 }
1751 self.end_graf();
1752 self.unsave();
1753 self.align_peek();
1754 }
1755 GroupCode::LocalBox => self.finish_local_box(),
1756 GroupCode::Vcenter | GroupCode::Math | GroupCode::MathChoice => self.math_right_brace(),
1757 _ => self.confusion("rightbrace"),
1758 }
1759 }
1760}
1761
1762pub fn glue_param_of_subtype(sub: u16) -> u32 {
1764 match sub {
1765 glue_sub::LINE_SKIP => gp::LINESKIP,
1766 glue_sub::BASELINE_SKIP => gp::BASELINESKIP,
1767 glue_sub::PAR_SKIP => gp::PARSKIP,
1768 glue_sub::ABOVE_DISPLAY => gp::ABOVEDISPLAYSKIP,
1769 glue_sub::BELOW_DISPLAY => gp::BELOWDISPLAYSKIP,
1770 glue_sub::ABOVE_DISPLAY_SHORT => gp::ABOVEDISPLAYSHORTSKIP,
1771 glue_sub::BELOW_DISPLAY_SHORT => gp::BELOWDISPLAYSHORTSKIP,
1772 glue_sub::LEFT_SKIP => gp::LEFTSKIP,
1773 glue_sub::RIGHT_SKIP => gp::RIGHTSKIP,
1774 glue_sub::TOP_SKIP => gp::TOPSKIP,
1775 glue_sub::SPLIT_TOP_SKIP => gp::SPLITTOPSKIP,
1776 glue_sub::TAB_SKIP => gp::TABSKIP,
1777 glue_sub::SPACE_SKIP => gp::SPACESKIP,
1778 glue_sub::XSPACE_SKIP => gp::XSPACESKIP,
1779 glue_sub::PAR_FILL_SKIP => gp::PARFILLSKIP,
1780 glue_sub::MATH_SKIP => gp::MATHSURROUNDSKIP,
1781 _ => gp::LINESKIP,
1782 }
1783}
1784
1785pub fn box_with(width: i32, height: i32, depth: i32) -> BoxData {
1787 BoxData {
1788 width,
1789 height,
1790 depth,
1791 ..BoxData::default()
1792 }
1793}