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);
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 let wp = self.eqtb.int(ip::WIDOWPENALTY);
1129 self.line_break(wp);
1130 }
1131 self.normal_paragraph();
1132 self.err.error_count = 0;
1133 }
1134 }
1135
1136 pub fn normal_paragraph(&mut self) {
1138 use crate::primitives_gen::set_etex_shape as es;
1139 if self.eqtb.int(ip::LOOSENESS) != 0 {
1140 self.eq_define(Loc::IntPar(ip::LOOSENESS), Val::Int(0));
1141 }
1142 if self.eqtb.dimen(dp::HANGINDENT) != 0 {
1143 self.eq_define(Loc::DimenPar(dp::HANGINDENT), Val::Int(0));
1144 }
1145 if self.eqtb.int(ip::HANGAFTER) != 1 {
1146 self.eq_define(Loc::IntPar(ip::HANGAFTER), Val::Int(1));
1147 }
1148 if self.eqtb.par_shape.val.is_some() {
1149 self.eq_define(Loc::ParShape, Val::Shape(None));
1150 }
1151 if self.eqtb.etex_shape[es::INTERLINEPENALTIES as usize]
1152 .val
1153 .is_some()
1154 {
1155 self.eq_define(Loc::EtexShape(es::INTERLINEPENALTIES), Val::Shape(None));
1156 }
1157 }
1158
1159 fn begin_insert_or_adjust(&mut self) {
1161 let v: i64 = if self.cur_cmd == Cmd::Vadjust {
1162 let pre = self.scan_keyword("pre");
1163 if pre { -2 } else { -1 }
1164 } else {
1165 self.scan_register_num();
1166 let mut n = self.cur_val;
1167 if n == self.output_box_num() {
1168 self.print_err("You can't ");
1169 self.print_esc_str("insert");
1170 self.printer.print_int(i64::from(n));
1171 self.help(&["I'm changing to \\insert0; box \\outputbox is special."]);
1172 self.error();
1173 n = 0;
1174 }
1175 i64::from(n)
1176 };
1177 self.push_saved(v);
1178 self.new_save_level(GroupCode::Insert);
1179 self.scan_left_brace();
1180 self.normal_paragraph();
1181 self.push_nest(Mode::InternalVertical);
1182 self.cur_list_mut().prev_depth = IGNORE_DEPTH;
1183 }
1184
1185 pub fn output_box_num(&self) -> i32 {
1187 let n = self.eqtb.int(ip::OUTPUTBOX);
1188 if (0..65536).contains(&n) { n } else { 255 }
1189 }
1190
1191 pub fn par_token_before_group_end(&mut self, level: i32) -> bool {
1197 if self.mode != Mode::Horizontal || self.eqtb.int(ip::PARTOKENCONTEXT) < level {
1198 return false;
1199 }
1200 self.back_input();
1201 self.cur_tok = Token::cs(self.par_token_cs);
1202 self.back_input();
1203 true
1204 }
1205
1206 fn finish_insert_group(&mut self) {
1208 if self.par_token_before_group_end(2) {
1209 return;
1210 }
1211 self.end_graf();
1212 let q = self.eqtb.glue(gp::SPLITTOPSKIP);
1213 let d = self.eqtb.dimen(dp::SPLITMAXDEPTH);
1214 let f = self.eqtb.int(ip::FLOATINGPENALTY);
1215 self.unsave();
1216 let n = self.pop_saved();
1217 let head = self.cur_list().head;
1218 let first = self.nodes.next(head);
1219 self.nodes.set_next(head, NodeRef::NULL);
1220 let p = self.vpack(first, PackSpec::NATURAL);
1221 self.pop_nest();
1222 let list = self.nodes.list_ptr(p);
1223 self.nodes.set_list_ptr(p, NodeRef::NULL);
1224 let (h, dd) = match self.nodes.box_data(p) {
1225 Some(b) => (b.height, b.depth),
1226 None => (0, 0),
1227 };
1228 if n >= 0 {
1229 let node = self.new_node(
1230 id::INS,
1231 n as u16,
1232 NodeData::Ins {
1233 cost: f,
1234 depth: d,
1235 height: h + dd,
1236 spec: q,
1237 head: list,
1238 },
1239 );
1240 self.tail_append(node);
1241 } else {
1242 let node = self.new_node(
1243 id::ADJUST,
1244 if n == -2 { 1 } else { 0 },
1245 NodeData::Adjust { head: list },
1246 );
1247 self.tail_append(node);
1248 }
1249 self.nodes.free(p);
1250 if self.nest.len() == 1 {
1251 self.build_page_from("insert");
1252 }
1253 }
1254
1255 fn make_mark(&mut self) {
1257 let c: u32 = if self.cur_chr == 0 {
1258 0
1259 } else if self.cur_chr == 1 {
1260 self.scan_register_num();
1262 let n = self.cur_val as u32;
1263 self.marks.classes.remove(&n);
1264 return;
1265 } else {
1266 self.scan_register_num();
1267 self.cur_val as u32
1268 };
1269 let toks = self.scan_toks_expanded();
1270 let p = self.new_node(
1271 id::MARK,
1272 0,
1273 NodeData::Mark {
1274 class: c,
1275 mark: std::rc::Rc::from(toks),
1276 },
1277 );
1278 self.tail_append(p);
1279 }
1280
1281 fn append_penalty(&mut self) {
1283 self.scan_int();
1284 let p = self.new_penalty_node(self.cur_val);
1285 self.tail_append(p);
1286 if self.mode == Mode::Vertical {
1287 self.build_page_from("penalty");
1288 }
1289 }
1290
1291 fn delete_last(&mut self) {
1293 let (head, tail) = (self.cur_list().head, self.cur_list().tail);
1294 let want = self.cur_chr as u8;
1295 if self.mode == Mode::Vertical && tail == head {
1296 if want != id::GLUE || self.page.last_glue.is_some() {
1298 self.you_cant();
1299 let l0 = if want == id::KERN {
1300 "Try `I\\kern-\\lastkern' instead."
1301 } else if want != id::GLUE {
1302 "Perhaps you can make the output routine do it."
1303 } else {
1304 "Try `I\\vskip-\\lastskip' instead."
1305 };
1306 self.help(&[
1307 "Sorry...I usually can't take things from the current page.",
1308 l0,
1309 ]);
1310 self.error();
1311 }
1312 return;
1313 }
1314 if tail == head || self.nodes.id(tail) != want {
1315 return;
1316 }
1317 let p = self.find_prev(tail);
1318 self.nodes.set_next(p, NodeRef::NULL);
1319 self.nodes.flush_list(tail);
1320 self.cur_list_mut().tail = p;
1321 }
1322
1323 fn unpackage(&mut self) {
1326 let c = self.cur_chr;
1327 if c > chr::COPY {
1328 let k = c as usize;
1330 let p = self.ts.disc_ptr[k];
1331 self.ts.disc_ptr[k] = NodeRef::NULL;
1332 if !p.is_null() {
1333 let tail = self.cur_list().tail;
1334 self.nodes.link(tail, p);
1335 let t = self.nodes.tail_of(p);
1336 self.cur_list_mut().tail = t;
1337 }
1338 return;
1339 }
1340 self.scan_register_num();
1341 let n = self.cur_val as usize;
1342 let p = self.eqtb.boxes[n].val;
1343 if p.is_null() {
1344 return;
1345 }
1346 let t = self.nodes.id(p);
1347 let k = self.mode.abs_kind();
1348 if k == ModeKind::Math
1349 || (k == ModeKind::Vertical && t != id::VLIST)
1350 || (k == ModeKind::Horizontal && t != id::HLIST)
1351 {
1352 self.print_err("Incompatible list can't be unboxed");
1353 self.help(&[
1354 "Sorry, Pandora. (You sneaky devil.)",
1355 "I refuse to unbox an \\hbox in vertical mode or vice versa.",
1356 "And I can't open any boxes in math mode.",
1357 ]);
1358 self.error();
1359 return;
1360 }
1361 let list = if c == chr::COPY {
1362 let l = self.nodes.list_ptr(p);
1363 self.nodes.copy_list(l)
1364 } else {
1365 let l = self.nodes.list_ptr(p);
1366 self.nodes.set_list_ptr(p, NodeRef::NULL);
1367 self.eqtb.boxes[n].val = NodeRef::NULL;
1368 self.nodes.free(p);
1369 l
1370 };
1371 let list = if t == id::HLIST {
1372 self.strip_margin_kerns(list)
1373 } else {
1374 list
1375 };
1376 if !list.is_null() {
1377 let tail = self.cur_list().tail;
1378 self.nodes.link(tail, list);
1379 let t = self.nodes.tail_of(list);
1380 self.cur_list_mut().tail = t;
1381 }
1382 }
1383
1384 fn append_italic_correction(&mut self) {
1386 let (head, tail) = (self.cur_list().head, self.cur_list().tail);
1387 if tail == head {
1388 return;
1389 }
1390 let Some(g) = self.nodes.glyph(tail) else {
1391 return;
1392 };
1393 let (f, c) = (g.font, g.chr);
1394 let ic = self.font_char_dimen(f, c, 3);
1395 let k = self.new_kern_node(ic, kern_sub::ITALIC);
1396 self.tail_append(k);
1397 }
1398
1399 fn append_discretionary(&mut self) {
1401 let c = self.cur_chr;
1402 if c == 1 {
1403 let pen = self.eqtb.int(ip::EXHYPHENPENALTY);
1405 let d = self.new_node(
1406 id::DISC,
1407 1,
1408 NodeData::Disc {
1409 pre: NodeRef::NULL,
1410 post: NodeRef::NULL,
1411 replace: NodeRef::NULL,
1412 penalty: pen,
1413 },
1414 );
1415 let lang = self.cur_lang();
1416 let (pre_c, post_c) = {
1417 let l = self.lang_params(lang);
1418 (l.pre_hyphen_char, l.post_hyphen_char)
1419 };
1420 let f = self.eqtb.cur_font.val;
1421 let pre = if pre_c > 0 {
1422 self.new_char_node(f, pre_c as u32)
1423 } else {
1424 NodeRef::NULL
1425 };
1426 let post = if post_c > 0 {
1427 self.new_char_node(f, post_c as u32)
1428 } else {
1429 NodeRef::NULL
1430 };
1431 self.nodes.set_disc_lists(d, (pre, post, NodeRef::NULL));
1432 self.tail_append(d);
1433 return;
1434 }
1435 let mut pen = self.eqtb.int(ip::HYPHENPENALTY);
1436 if self.scan_keyword("penalty") {
1437 self.scan_int();
1438 pen = self.cur_val;
1439 }
1440 let d = self.new_node(
1441 id::DISC,
1442 if c == 2 { 2 } else { 0 },
1443 NodeData::Disc {
1444 pre: NodeRef::NULL,
1445 post: NodeRef::NULL,
1446 replace: NodeRef::NULL,
1447 penalty: pen,
1448 },
1449 );
1450 self.tail_append(d);
1451 self.push_saved(0);
1452 self.new_save_level(GroupCode::Disc);
1453 self.scan_left_brace();
1454 self.push_nest(Mode::RestrictedHorizontal);
1455 self.cur_list_mut().space_factor = 1000;
1456 }
1457
1458 fn build_discretionary(&mut self) {
1461 self.unsave();
1462 let head = self.cur_list().head;
1464 let mut q = head;
1465 let mut p = self.nodes.next(q);
1466 while !p.is_null() {
1467 let t = self.nodes.id(p);
1468 if !matches!(t, id::GLYPH | id::KERN | id::HLIST | id::VLIST | id::RULE) {
1469 self.print_err("Improper discretionary list");
1470 self.help(&["Discretionary lists must contain only boxes and kerns."]);
1471 self.error();
1472 self.begin_diagnostic();
1473 self.printer
1474 .print_nl("The following discretionary sublist has been deleted:");
1475 self.show_box(p);
1476 self.end_diagnostic(true);
1477 self.nodes.flush_list(p);
1478 self.nodes.set_next(q, NodeRef::NULL);
1479 break;
1480 }
1481 q = p;
1482 p = self.nodes.next(q);
1483 }
1484 let list = self.nodes.next(head);
1485 self.nodes.set_next(head, NodeRef::NULL);
1486 if !list.is_null() {
1487 self.nodes.set_prev(list, NodeRef::NULL);
1488 }
1489 self.pop_nest();
1490 let k = self.pop_saved();
1491 let d = self.cur_list().tail;
1492 let (mut pre, mut post, mut rep) = self.nodes.disc_lists(d);
1493 match k {
1494 0 => pre = list,
1495 1 => post = list,
1496 _ => {
1497 if !list.is_null() && self.mode.abs_kind() == ModeKind::Math {
1498 self.print_err("Illegal math ");
1499 self.print_esc_str("discretionary");
1500 self.help(&[
1501 "Sorry: The third part of a discretionary break must be",
1502 "empty, in math formulas. I had to delete your third part.",
1503 ]);
1504 self.nodes.flush_list(list);
1505 self.error();
1506 } else {
1507 rep = list;
1508 }
1509 self.nodes.set_disc_lists(d, (pre, post, rep));
1510 return;
1511 }
1512 }
1513 self.nodes.set_disc_lists(d, (pre, post, rep));
1514 self.push_saved(k + 1);
1515 self.new_save_level(GroupCode::Disc);
1516 self.scan_left_brace();
1517 self.push_nest(Mode::RestrictedHorizontal);
1518 self.cur_list_mut().space_factor = 1000;
1519 }
1520
1521 fn make_accent(&mut self) {
1523 self.scan_char_num();
1524 let f = self.eqtb.cur_font.val;
1525 let p = self.new_char_node(f, self.cur_val as u32);
1526 let x = self.x_height(f);
1527 let s = f64::from(self.font(f).params.get(1).copied().unwrap_or(0)) / 65536.0;
1528 let a = self.char_width(f, self.cur_val as u32);
1529 self.do_assignments();
1530 let mut q = NodeRef::NULL;
1531 let f2 = self.eqtb.cur_font.val;
1532 if matches!(self.cur_cmd, Cmd::Letter | Cmd::OtherChar | Cmd::CharGiven) {
1533 q = self.new_char_node(f2, self.cur_chr);
1534 } else if self.cur_cmd == Cmd::CharNum {
1535 self.scan_char_num();
1536 q = self.new_char_node(f2, self.cur_val as u32);
1537 } else {
1538 self.back_input();
1539 }
1540 let mut p = p;
1541 if !q.is_null() {
1542 let t = f64::from(self.font(f2).params.get(1).copied().unwrap_or(0)) / 65536.0;
1543 let qc = self.nodes.glyph(q).map_or(0, |g| g.chr);
1544 let w = self.char_width(f2, qc);
1545 let h = self.font_char_dimen(f2, qc, 1);
1546 if h != x {
1547 p = self.hpack(p, PackSpec::NATURAL);
1548 if let Some(b) = self.nodes.box_data_mut(p) {
1549 b.shift = x - h;
1550 }
1551 }
1552 let delta =
1553 ((f64::from(w - a)) / 2.0 + f64::from(h) * t - f64::from(x) * s).round() as i32;
1554 let r = self.new_kern_node(delta, kern_sub::ACCENT);
1555 self.tail_append(r);
1556 self.tail_append(p);
1557 let k = self.new_kern_node(-a - delta, kern_sub::ACCENT);
1558 self.tail_append(k);
1559 p = q;
1560 }
1561 self.tail_append(p);
1562 self.cur_list_mut().space_factor = 1000;
1563 }
1564
1565 #[inline]
1567 pub fn char_width(&self, f: u32, c: u32) -> i32 {
1568 self.font(f).chars.get(&c).map_or(0, |m| m.width)
1569 }
1570
1571 pub fn do_assignments(&mut self) {
1573 loop {
1574 self.get_nonblank_nonrelax_noncall();
1575 if self.cur_cmd <= Cmd::MAX_NON_PREFIXED {
1576 return;
1577 }
1578 self.err.set_box_allowed = false;
1579 self.prefixed_command();
1580 self.err.set_box_allowed = true;
1581 }
1582 }
1583
1584 pub fn append_boundary(&mut self) {
1587 let c = self.cur_chr;
1588 let v = if c == 1 || c == 2 {
1589 self.scan_int();
1590 self.cur_val
1591 } else {
1592 0
1593 };
1594 let p = self.new_node(id::BOUNDARY, c as u16, NodeData::Boundary { value: v });
1595 self.tail_append(p);
1596 }
1597
1598 fn append_ghost(&mut self) {
1603 self.get_x_token();
1604 }
1605
1606 fn begin_local_box(&mut self) {
1608 let k = self.cur_chr;
1609 self.push_saved(i64::from(k));
1610 self.new_save_level(GroupCode::LocalBox);
1611 self.scan_left_brace();
1612 self.push_nest(Mode::RestrictedHorizontal);
1613 self.cur_list_mut().space_factor = 1000;
1614 }
1615
1616 fn finish_local_box(&mut self) {
1618 self.unsave();
1619 let k = self.pop_saved() as usize;
1620 let head = self.cur_list().head;
1621 let first = self.nodes.next(head);
1622 self.nodes.set_next(head, NodeRef::NULL);
1623 self.pop_nest();
1624 let b = if first.is_null() {
1625 NodeRef::NULL
1626 } else {
1627 let first = self.node_process_list(first, crate::nodeproc::Context::Hbox);
1628 let b = self.hpack(first, PackSpec::NATURAL);
1629 self.nodes.set_subtype(b, 2);
1630 b
1631 };
1632 self.eq_define(Loc::LocalBox(k as u32), Val::Node(b));
1633 if self.mode == Mode::Horizontal {
1634 let lp = self.new_local_par();
1636 self.tail_append(lp);
1637 }
1638 }
1639
1640 fn align_error(&mut self) {
1642 if self.input.align_state.abs() > 2 {
1643 self.print_err("Misplaced ");
1644 self.print_cmd_chr(self.cur_cmd, self.cur_chr);
1645 if self.cur_tok == Token::char(Cmd::TabMark, '&' as u32) {
1646 self.help(&[
1647 "I can't figure out why you would want to use a tab mark",
1648 "here. If you just want an ampersand, the remedy is",
1649 "simple: Just type `I\\&' now. But if some right brace",
1650 "up above has ended a previous alignment prematurely,",
1651 "you're probably due for more error messages, and you",
1652 "might try typing `S' now just to see what is salvageable.",
1653 ]);
1654 } else {
1655 self.help(&[
1656 "I can't figure out why you would want to use a tab mark",
1657 "or \\cr or \\span just now. If something like a right brace",
1658 "up above has ended a previous alignment prematurely,",
1659 "you're probably due for more error messages, and you",
1660 "might try typing `S' now just to see what is salvageable.",
1661 ]);
1662 }
1663 self.error();
1664 } else {
1665 self.back_input();
1666 if self.input.align_state < 0 {
1667 self.print_err("Missing { inserted");
1668 self.input.align_state += 1;
1669 self.cur_tok = Token::LEFT_BRACE;
1670 } else {
1671 self.print_err("Missing } inserted");
1672 self.input.align_state -= 1;
1673 self.cur_tok = Token::RIGHT_BRACE;
1674 }
1675 self.help(&[
1676 "I've put in what seems to be necessary to fix",
1677 "the current column of the current alignment.",
1678 "Try to go on, since this might almost work.",
1679 ]);
1680 self.ins_error();
1681 }
1682 }
1683
1684 fn no_align_error(&mut self) {
1685 self.print_err("Misplaced ");
1686 self.print_esc_str("noalign");
1687 self.help(&[
1688 "I expect to see \\noalign only after the \\cr of",
1689 "an alignment. Proceed, and I'll ignore this case.",
1690 ]);
1691 self.error();
1692 }
1693
1694 fn omit_error(&mut self) {
1695 self.print_err("Misplaced ");
1696 self.print_esc_str("omit");
1697 self.help(&[
1698 "I expect to see \\omit only after tab marks or the \\cr of",
1699 "an alignment. Proceed, and I'll ignore this case.",
1700 ]);
1701 self.error();
1702 }
1703
1704 fn cs_error(&mut self) {
1705 self.print_err("Extra ");
1706 self.print_esc_str("endcsname");
1707 self.help(&["I'm ignoring this, since I wasn't doing a \\csname."]);
1708 self.error();
1709 }
1710
1711 pub fn append_to_vlist(&mut self, b: NodeRef) {
1714 let pd = self.cur_list().prev_depth;
1715 if pd > IGNORE_DEPTH {
1716 let bs = self.eqtb.glue(gp::BASELINESKIP);
1717 let h = self.node_height(b);
1718 let d = bs.width - pd - h;
1719 let p = if d < self.eqtb.dimen(dp::LINESKIPLIMIT) {
1720 self.new_param_glue(glue_sub::LINE_SKIP)
1721 } else {
1722 let g = Glue { width: d, ..bs };
1723 self.new_glue_node(g, glue_sub::BASELINE_SKIP)
1724 };
1725 self.tail_append(p);
1726 }
1727 self.tail_append(b);
1728 let d = self.node_depth(b);
1729 self.cur_list_mut().prev_depth = d;
1730 }
1731
1732 pub fn handle_right_brace_typesetting(&mut self) {
1735 match self.save.cur_group {
1736 GroupCode::Hbox => self.package(false),
1737 GroupCode::AdjustedHbox => {
1738 self.start_adjust_lists();
1739 self.package(false);
1740 }
1741 GroupCode::Vbox => {
1742 if self.par_token_before_group_end(1) {
1743 return;
1744 }
1745 self.end_graf();
1746 self.package(false);
1747 }
1748 GroupCode::Vtop => {
1749 if self.par_token_before_group_end(1) {
1750 return;
1751 }
1752 self.end_graf();
1753 self.package(true);
1754 }
1755 GroupCode::Insert => self.finish_insert_group(),
1756 GroupCode::Output => self.resume_output(),
1757 GroupCode::Disc => self.build_discretionary(),
1758 GroupCode::Align => {
1759 self.back_input();
1760 self.cur_tok = Token::cs(self.cs.frozen_cr);
1761 self.print_err("Missing ");
1762 self.print_esc_str("cr");
1763 self.printer.print_str(" inserted");
1764 self.help(&["I'm guessing that you meant to end an alignment here."]);
1765 self.ins_error();
1766 }
1767 GroupCode::NoAlign => {
1768 if self.par_token_before_group_end(2) {
1769 return;
1770 }
1771 self.end_graf();
1772 self.unsave();
1773 self.align_peek();
1774 }
1775 GroupCode::LocalBox => self.finish_local_box(),
1776 GroupCode::Vcenter | GroupCode::Math | GroupCode::MathChoice => self.math_right_brace(),
1777 _ => self.confusion("rightbrace"),
1778 }
1779 }
1780}
1781
1782pub fn glue_param_of_subtype(sub: u16) -> u32 {
1784 match sub {
1785 glue_sub::LINE_SKIP => gp::LINESKIP,
1786 glue_sub::BASELINE_SKIP => gp::BASELINESKIP,
1787 glue_sub::PAR_SKIP => gp::PARSKIP,
1788 glue_sub::ABOVE_DISPLAY => gp::ABOVEDISPLAYSKIP,
1789 glue_sub::BELOW_DISPLAY => gp::BELOWDISPLAYSKIP,
1790 glue_sub::ABOVE_DISPLAY_SHORT => gp::ABOVEDISPLAYSHORTSKIP,
1791 glue_sub::BELOW_DISPLAY_SHORT => gp::BELOWDISPLAYSHORTSKIP,
1792 glue_sub::LEFT_SKIP => gp::LEFTSKIP,
1793 glue_sub::RIGHT_SKIP => gp::RIGHTSKIP,
1794 glue_sub::TOP_SKIP => gp::TOPSKIP,
1795 glue_sub::SPLIT_TOP_SKIP => gp::SPLITTOPSKIP,
1796 glue_sub::TAB_SKIP => gp::TABSKIP,
1797 glue_sub::SPACE_SKIP => gp::SPACESKIP,
1798 glue_sub::XSPACE_SKIP => gp::XSPACESKIP,
1799 glue_sub::PAR_FILL_SKIP => gp::PARFILLSKIP,
1800 glue_sub::MATH_SKIP => gp::MATHSURROUNDSKIP,
1801 _ => gp::LINESKIP,
1802 }
1803}
1804
1805pub fn box_with(width: i32, height: i32, depth: i32) -> BoxData {
1807 BoxData {
1808 width,
1809 height,
1810 depth,
1811 ..BoxData::default()
1812 }
1813}