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