1use std::rc::Rc;
8
9use crate::Engine;
10use crate::build::PackSpec;
11use crate::cmd::Cmd;
12use crate::eqtb::{Loc, Val};
13use crate::glue::Glue;
14use crate::input::{Level, ScannerStatus, TokKind};
15use crate::maincontrol::{IGNORE_DEPTH, Mode, ModeKind};
16use crate::nodes::{BoxData, NodeData, NodeRef, glue_sub, id};
17use crate::primitives_gen::{assign_dimen as dp, assign_glue as gp, assign_int as ip};
18use crate::save::GroupCode;
19use crate::token::{Token, TokenList};
20use crate::typeset::NULL_FLAG;
21
22pub const SPAN_CODE: u32 = 0x11_0002;
24pub const CR_CODE: u32 = 0x11_0003;
26pub const CR_CR_CODE: u32 = 0x11_0004;
28
29#[derive(Clone, Debug)]
31struct Column {
32 u: TokenList,
33 v: TokenList,
34 width: i32,
36 spans: Vec<(usize, i32)>,
39 extra_info: u32,
41}
42
43#[derive(Debug)]
46struct Level0 {
47 cols: Vec<Column>,
48 tabskips: Vec<Glue>,
50 cur_align: Option<usize>,
51 cur_span: usize,
52 cur_loop: Option<usize>,
55 saved_align_state: i32,
56 adj_head: NodeRef,
58 adj_tail: NodeRef,
59 outer: Option<Box<Level0>>,
61}
62
63#[derive(Debug, Default)]
65pub struct AlignState {
66 cur: Option<Box<Level0>>,
67}
68
69impl Engine {
70 fn al(&mut self) -> &mut Level0 {
71 self.ts.align.cur.as_mut().expect("alignment level present")
72 }
73
74 fn al_ref(&self) -> Option<&Level0> {
75 self.ts.align.cur.as_deref()
76 }
77
78 fn push_alignment(&mut self) {
80 let outer = self.ts.align.cur.take();
81 let adj_head = self.nodes.new_temp();
82 self.ts.align.cur = Some(Box::new(Level0 {
83 cols: Vec::new(),
84 tabskips: Vec::new(),
85 cur_align: None,
86 cur_span: 0,
87 cur_loop: None,
88 saved_align_state: self.input.align_state,
89 adj_head,
90 adj_tail: adj_head,
91 outer,
92 }));
93 }
94
95 fn pop_alignment(&mut self) {
97 if let Some(l) = self.ts.align.cur.take() {
98 self.nodes.free(l.adj_head);
99 self.input.align_state = l.saved_align_state;
100 self.ts.align.cur = l.outer;
101 }
102 }
103
104 pub fn init_align(&mut self) {
106 let save_cs_ptr = self.cur_cs;
107 self.push_alignment();
108 self.input.align_state = -1_000_000;
109 if self.mode == Mode::DisplayMath && self.cur_list().head != self.cur_list().tail {
110 self.print_err("Improper ");
111 self.print_esc_str("halign");
112 self.printer.print_str(" inside $$'s");
113 self.help(&[
114 "Displays can use special alignments (like \\eqalignno)",
115 "only if nothing but the alignment itself is between $$'s.",
116 "So I've deleted the formulas that preceded this alignment.",
117 ]);
118 self.error();
119 self.flush_math();
120 }
121 let outer_pd = self.cur_list().prev_depth;
122 let outer_mode = self.mode;
123 let new_mode = match outer_mode {
124 Mode::DisplayMath => Mode::InternalVertical,
125 Mode::Vertical | Mode::InternalVertical => Mode::InternalVertical,
126 _ => Mode::RestrictedHorizontal,
127 };
128 self.push_nest(new_mode);
129 if new_mode == Mode::InternalVertical {
130 self.cur_list_mut().prev_depth = if outer_mode == Mode::DisplayMath {
131 let n = self.nest.len();
132 self.nest[n.saturating_sub(3)].prev_depth
133 } else {
134 outer_pd
135 };
136 }
137 self.scan_spec(GroupCode::Align, None);
138 self.input.scanner_status = ScannerStatus::Aligning;
140 self.input.warning_index = save_cs_ptr;
141 self.input.align_state = -1_000_000;
142 loop {
143 let g = self.eqtb.glue(gp::TABSKIP);
144 self.al().tabskips.push(g);
145 if self.cur_cmd == Cmd::CarRet {
146 break;
147 }
148 let mut u: Vec<Token> = Vec::new();
150 loop {
151 self.get_preamble_token();
152 if self.cur_cmd == Cmd::MacParam {
153 break;
154 }
155 if self.cur_cmd <= Cmd::CarRet
156 && self.cur_cmd >= Cmd::TabMark
157 && self.input.align_state == -1_000_000
158 {
159 let ncols = self.al().cols.len();
160 if u.is_empty() && self.al().cur_loop.is_none() && self.cur_cmd == Cmd::TabMark
161 {
162 self.al().cur_loop = Some(ncols);
163 } else {
164 self.print_err("Missing # inserted in alignment preamble");
165 self.help(&[
166 "There should be exactly one # between &'s, when an",
167 "\\halign or \\valign is being set up. In this case you had",
168 "none, so I've put one in; maybe that will work.",
169 ]);
170 self.back_error();
171 break;
172 }
173 } else if self.cur_cmd != Cmd::Spacer || !u.is_empty() {
174 u.push(self.cur_tok);
175 }
176 }
177 let mut v: Vec<Token> = Vec::new();
179 loop {
180 self.get_preamble_token();
181 if self.cur_cmd <= Cmd::CarRet
182 && self.cur_cmd >= Cmd::TabMark
183 && self.input.align_state == -1_000_000
184 {
185 break;
186 }
187 if self.cur_cmd == Cmd::MacParam {
188 self.print_err("Only one # is allowed per tab");
189 self.help(&[
190 "There should be exactly one # between &'s, when an",
191 "\\halign or \\valign is being set up. In this case you had",
192 "more than one, so I'm ignoring all but the first.",
193 ]);
194 self.error();
195 continue;
196 }
197 v.push(self.cur_tok);
198 }
199 v.push(Token::cs(self.cs.frozen_end_template));
200 self.al().cols.push(Column {
201 u: Rc::from(u),
202 v: Rc::from(v),
203 width: NULL_FLAG,
204 spans: Vec::new(),
205 extra_info: 0,
206 });
207 }
208 self.input.scanner_status = ScannerStatus::Normal;
209 self.new_save_level(GroupCode::Align);
210 let t = self
211 .eqtb
212 .toks_param(crate::primitives_gen::assign_toks::EVERYCR)
213 .clone();
214 if !t.is_empty() {
215 self.begin_token_list(t, TokKind::EveryCrText);
216 }
217 self.align_peek();
218 }
219
220 fn get_preamble_token(&mut self) {
223 loop {
224 self.get_token();
225 while self.cur_chr == SPAN_CODE && self.cur_cmd == Cmd::TabMark {
226 self.get_token();
227 if self.cur_cmd > Cmd::MAX_COMMAND {
228 self.expand();
229 self.get_token();
230 }
231 }
232 if self.cur_cmd == Cmd::Endv {
233 self.fatal_error("(interwoven alignment preambles are not allowed)");
234 }
235 if self.cur_cmd == Cmd::AssignGlue && self.cur_chr == gp::TABSKIP {
236 self.scan_optional_equals();
237 self.scan_glue(crate::scan::ValLevel::Glue);
238 let g = self.cur_glue;
239 if self.eqtb.int(ip::GLOBALDEFS) > 0 {
240 self.geq_define(Loc::GluePar(gp::TABSKIP), Val::Glue(g));
241 } else {
242 self.eq_define(Loc::GluePar(gp::TABSKIP), Val::Glue(g));
243 }
244 continue;
245 }
246 return;
247 }
248 }
249
250 pub fn align_peek(&mut self) {
253 loop {
254 self.input.align_state = 1_000_000;
255 self.get_nonblank_noncall();
256 if self.cur_cmd == Cmd::NoAlign {
257 self.scan_left_brace();
258 self.new_save_level(GroupCode::NoAlign);
259 if self.mode == Mode::InternalVertical {
260 self.normal_paragraph();
261 }
262 } else if self.cur_cmd == Cmd::RightBrace {
263 self.fin_align();
264 } else if self.cur_cmd == Cmd::CarRet && self.cur_chr == CR_CR_CODE {
265 continue;
266 } else {
267 self.init_row();
268 self.init_col();
269 }
270 return;
271 }
272 }
273
274 fn init_row(&mut self) {
276 let h = self.mode == Mode::InternalVertical;
277 self.push_nest(if h {
278 Mode::RestrictedHorizontal
279 } else {
280 Mode::InternalVertical
281 });
282 if h {
283 self.cur_list_mut().space_factor = 0;
284 } else {
285 self.cur_list_mut().prev_depth = 0;
286 }
287 let g = self.al().tabskips[0];
288 let p = self.new_glue_node(g, glue_sub::TAB_SKIP);
289 self.tail_append(p);
290 let l = self.al();
291 l.cur_align = Some(0);
292 l.adj_tail = l.adj_head;
293 self.init_span(0);
294 }
295
296 fn init_span(&mut self, p: usize) {
298 let h = self.mode == Mode::RestrictedHorizontal;
299 self.push_nest(if h {
300 Mode::RestrictedHorizontal
301 } else {
302 Mode::InternalVertical
303 });
304 if h {
305 self.cur_list_mut().space_factor = 1000;
306 } else {
307 self.cur_list_mut().prev_depth = IGNORE_DEPTH;
308 self.normal_paragraph();
309 }
310 self.al().cur_span = p;
311 }
312
313 fn init_col(&mut self) {
315 let cmd = self.cur_cmd;
316 let ca = self.al().cur_align.unwrap_or(0);
317 self.al().cols[ca].extra_info = cmd as u32;
318 if cmd == Cmd::Omit {
319 self.input.align_state = 0;
320 } else {
321 self.back_input();
322 let u = self.al().cols[ca].u.clone();
323 self.begin_token_list(u, TokKind::UTemplate);
324 }
325 }
326
327 pub fn insert_v_template(&mut self) {
329 let ca = self.al_ref().and_then(|l| l.cur_align);
330 let Some(ca) = ca else {
331 self.fatal_error("(interwoven alignment preambles are not allowed)");
332 };
333 if self.input.scanner_status == ScannerStatus::Aligning {
334 self.fatal_error("(interwoven alignment preambles are not allowed)");
335 }
336 let extra = self.al().cols[ca].extra_info;
337 self.al().cols[ca].extra_info = self.cur_chr;
338 if extra == Cmd::Omit as u32 {
339 let t: TokenList = Rc::from(vec![Token::cs(self.cs.frozen_end_template)]);
340 self.begin_token_list(t, TokKind::VTemplate);
341 } else {
342 let v = self.al().cols[ca].v.clone();
343 self.begin_token_list(v, TokKind::VTemplate);
344 }
345 self.input.align_state = 1_000_000;
346 }
347
348 pub fn do_endv(&mut self) {
350 let mut ok = false;
352 for lvl in self.input.stack.iter().rev() {
353 match lvl {
354 Level::Tokens(t) if t.kind != TokKind::VTemplate && t.loc >= t.list.len() => {
355 continue;
356 }
357 Level::Tokens(t) => {
358 ok = t.kind == TokKind::VTemplate && t.loc >= t.list.len();
359 break;
360 }
361 Level::Text(_) => break,
362 }
363 }
364 if !ok {
365 self.fatal_error("(interwoven alignment preambles are not allowed)");
366 }
367 if self.save.cur_group == GroupCode::Align {
368 self.end_graf();
369 if self.fin_col() {
370 self.fin_row();
371 }
372 } else {
373 self.off_save();
374 }
375 }
376
377 fn fin_col(&mut self) -> bool {
380 let Some(ca) = self.al_ref().and_then(|l| l.cur_align) else {
381 self.confusion("endv");
382 };
383 if self.input.align_state < 500_000 {
384 self.fatal_error("(interwoven alignment preambles are not allowed)");
385 }
386 let ncols = self.al().cols.len();
387 let mut p = ca + 1;
388 if p >= ncols && self.al().cols[ca].extra_info < CR_CODE {
391 if let Some(cl) = self.al().cur_loop {
392 let l = self.al();
394 let src = l.cols[cl].clone();
395 l.cols.push(Column {
396 u: src.u,
397 v: src.v,
398 width: NULL_FLAG,
399 spans: Vec::new(),
400 extra_info: 0,
401 });
402 let g = l.tabskips[cl + 1];
403 l.tabskips.push(g);
404 l.cur_loop = Some(cl + 1);
405 p = l.cols.len() - 1;
406 } else {
407 self.print_err("Extra alignment tab has been changed to ");
408 self.print_esc_str("cr");
409 self.help(&[
410 "You have given more \\span or & marks than there were",
411 "in the preamble to the \\halign or \\valign now in progress.",
412 "So I'll assume that you meant to type \\cr instead.",
413 ]);
414 self.al().cols[ca].extra_info = CR_CODE;
415 self.error();
416 }
417 }
418 if self.al().cols[ca].extra_info != SPAN_CODE {
419 self.unsave();
420 self.new_save_level(GroupCode::Align);
421 let head = self.cur_list().head;
423 let first = self.nodes.next(head);
424 self.nodes.set_next(head, NodeRef::NULL);
425 let h = self.mode == Mode::RestrictedHorizontal;
426 let (u, w) = if h {
427 let first = self.node_process_list(first, crate::nodeproc::Context::Hbox);
428 let first = self.filter_list("hpack_filter", first, "align_set");
429 let at = self.al().adj_tail;
430 self.ts.adjust_tail = Some(at);
431 self.ts.pre_adjust_tail = None;
432 let u = self.hpack(first, PackSpec::NATURAL);
433 let nt = self.ts.adjust_tail.take().unwrap_or(at);
434 self.al().adj_tail = nt;
435 let w = self.nodes.width(u);
436 (u, w)
437 } else {
438 let u = self.vpackage(first, PackSpec::NATURAL, 0);
439 (u, self.node_height(u))
440 };
441 let cs = self.al().cur_span;
442 let mut n = 0usize;
443 if cs != ca {
444 n = ca - cs;
446 let col = &mut self.al().cols[cs];
447 match col.spans.iter_mut().find(|(k, _)| *k == n) {
448 Some(e) => {
449 if e.1 < w {
450 e.1 = w;
451 }
452 }
453 None => {
454 col.spans.push((n, w));
455 col.spans.sort_by_key(|e| e.0);
456 }
457 }
458 } else if w > self.al().cols[ca].width {
459 self.al().cols[ca].width = w;
460 }
461 let (st, sh) = self.ts.last_totals;
462 let so = (1..5).rev().find(|&o| st[o] != 0).unwrap_or(0);
463 let ho = (1..5).rev().find(|&o| sh[o] != 0).unwrap_or(0);
464 self.make_unset(u, n as u16, st[so], so as u8, sh[ho], ho as u8);
465 self.pop_nest();
466 self.tail_append(u);
467 let g = self.al().tabskips[ca + 1];
469 let gn = self.new_glue_node(g, glue_sub::TAB_SKIP);
470 self.tail_append(gn);
471 if self.al().cols[ca].extra_info >= CR_CODE {
472 return true;
473 }
474 self.init_span(p);
475 }
476 self.input.align_state = 1_000_000;
477 self.get_nonblank_noncall();
478 self.al().cur_align = Some(p);
479 self.init_col();
480 false
481 }
482
483 fn make_unset(&mut self, u: NodeRef, span: u16, st: i32, so: u8, sh: i32, ho: u8) {
485 let b = match self.nodes.data(u) {
486 NodeData::Box(b) => (**b).clone(),
487 _ => BoxData::default(),
488 };
489 let n = self.nodes.get_mut(u);
490 n.id = id::UNSET;
491 n.subtype = 0;
492 n.data = NodeData::Unset {
493 b: Box::new(BoxData {
494 glue_set: 0.0,
495 glue_sign: 0,
496 glue_order: 0,
497 shift: 0,
498 ..b
499 }),
500 span_count: span,
501 stretch: st,
502 shrink: sh,
503 stretch_order: so,
504 shrink_order: ho,
505 };
506 }
507
508 fn fin_row(&mut self) {
510 let head = self.cur_list().head;
511 let first = self.nodes.next(head);
512 self.nodes.set_next(head, NodeRef::NULL);
513 let p = if self.mode == Mode::RestrictedHorizontal {
514 let p = self.hpack(first, PackSpec::NATURAL);
515 self.pop_nest();
516 self.append_to_vlist(p);
517 let (ah, at) = (self.al().adj_head, self.al().adj_tail);
518 if ah != at {
519 let f = self.nodes.next(ah);
520 let tail = self.cur_list().tail;
521 self.nodes.link(tail, f);
522 self.cur_list_mut().tail = at;
523 self.nodes.set_next(ah, NodeRef::NULL);
524 self.al().adj_tail = ah;
525 }
526 p
527 } else {
528 let p = self.vpack(first, PackSpec::NATURAL);
529 self.pop_nest();
530 self.tail_append(p);
531 self.cur_list_mut().space_factor = 1000;
532 p
533 };
534 self.make_unset(p, 0, 0, 0, 0, 0);
535 let t = self
536 .eqtb
537 .toks_param(crate::primitives_gen::assign_toks::EVERYCR)
538 .clone();
539 if !t.is_empty() {
540 self.begin_token_list(t, TokKind::EveryCrText);
541 }
542 self.align_peek();
543 }
544
545 fn fin_align(&mut self) {
547 if self.save.cur_group != GroupCode::Align {
548 self.confusion("align1");
549 }
550 self.unsave();
551 if self.save.cur_group != GroupCode::Align {
552 self.confusion("align0");
553 }
554 self.unsave();
555 let n = self.nest.len();
556 let o = if n >= 2 && self.nest[n - 2].mode == Mode::DisplayMath {
557 self.eqtb.dimen(dp::DISPLAYINDENT)
558 } else {
559 0
560 };
561 let ncols = self.al().cols.len();
563 let mut widths: Vec<i32> = self.al().cols.iter().map(|c| c.width).collect();
564 let mut spans: Vec<Vec<(usize, i32)>> =
565 self.al().cols.iter().map(|c| c.spans.clone()).collect();
566 let mut tabs: Vec<Glue> = self.al().tabskips.clone();
567 for q in 0..ncols {
568 if widths[q] == NULL_FLAG {
569 widths[q] = 0;
570 tabs[q + 1] = Glue::ZERO;
571 }
572 let mine = std::mem::take(&mut spans[q]);
573 if mine.is_empty() || q + 1 >= ncols {
574 continue;
575 }
576 let t = widths[q] + tabs[q + 1].width;
578 for (k, w) in mine {
579 let w = w - t;
580 if k <= 1 {
581 if w > widths[q + 1] {
582 widths[q + 1] = w;
583 }
584 } else {
585 match spans[q + 1].iter_mut().find(|(kk, _)| *kk == k - 1) {
586 Some(e) => {
587 if w > e.1 {
588 e.1 = w;
589 }
590 }
591 None => spans[q + 1].push((k - 1, w)),
592 }
593 }
594 }
595 }
596 let (spec, _dir) = {
598 let dir = self.pop_saved();
599 let v = self.pop_saved() as i32;
600 let code = self.pop_saved();
601 (
602 if code == 0 {
603 PackSpec::Exactly(v)
604 } else {
605 PackSpec::Additional(v)
606 },
607 dir,
608 )
609 };
610 let hmode = self.mode == Mode::InternalVertical;
611 let mut plist = NodeRef::NULL;
612 let mut ptail = NodeRef::NULL;
613 let mut push = |e: &mut Engine, n: NodeRef| {
614 if plist.is_null() {
615 plist = n;
616 } else {
617 e.nodes.link(ptail, n);
618 }
619 ptail = n;
620 };
621 for k in 0..=ncols {
622 let g = self.nodes.new_glue(tabs[k], glue_sub::TAB_SKIP);
623 push(self, g);
624 if k < ncols {
625 let u = self.nodes.new_null_box(true);
626 if let Some(b) = self.nodes.box_data_mut(u) {
627 if hmode {
628 b.width = widths[k];
629 } else {
630 b.height = widths[k];
631 }
632 }
633 self.make_unset(u, 0, 0, 0, 0, 0);
634 push(self, u);
635 }
636 }
637 self.ts.pack_begin_line = -self.cur_list().mode_line;
638 let p = if hmode {
639 let rule_save = self.eqtb.dimen_par[dp::OVERFULLRULE as usize].val;
640 self.eqtb.dimen_par[dp::OVERFULLRULE as usize].val = 0;
641 let p = self.hpack(plist, spec);
642 self.eqtb.dimen_par[dp::OVERFULLRULE as usize].val = rule_save;
643 p
644 } else {
645 let p = self.vpack(plist, spec);
646 let mut q = self.nodes.next(self.nodes.list_ptr(p));
648 while !q.is_null() {
649 if let Some(b) = self.nodes.box_data_mut(q) {
650 b.width = b.height;
651 b.height = 0;
652 }
653 q = self.nodes.next(q);
654 if !q.is_null() {
655 q = self.nodes.next(q);
656 }
657 }
658 p
659 };
660 self.ts.pack_begin_line = 0;
661 let pb = self.nodes.box_data(p).cloned().unwrap_or_default();
662 let head = self.cur_list().head;
664 let mut s = head;
665 let mut q = self.nodes.next(head);
666 while !q.is_null() {
667 match self.nodes.id(q) {
668 id::UNSET => self.set_unset_row(q, &pb, o, hmode),
669 id::RULE => {
670 let (pw, ph, pd) = (pb.width, pb.height, pb.depth);
671 if let NodeData::Rule {
672 width,
673 height,
674 depth,
675 ..
676 } = self.nodes.data_mut(q)
677 {
678 if *width == NULL_FLAG {
679 *width = pw;
680 }
681 if *height == NULL_FLAG {
682 *height = ph;
683 }
684 if *depth == NULL_FLAG {
685 *depth = pd;
686 }
687 }
688 if o != 0 {
689 let r = self.nodes.next(q);
690 self.nodes.set_next(q, NodeRef::NULL);
691 let b = self.hpack(q, PackSpec::NATURAL);
692 if let Some(bd) = self.nodes.box_data_mut(b) {
693 bd.shift = o;
694 }
695 self.nodes.link(b, r);
696 self.nodes.link(s, b);
697 q = b;
698 }
699 }
700 _ => {}
701 }
702 s = q;
703 q = self.nodes.next(q);
704 }
705 self.nodes.flush_list(p);
706 self.pop_alignment();
707 let pd_save = self.cur_list().prev_depth;
709 let sf_save = self.cur_list().space_factor;
710 let head = self.cur_list().head;
711 let first = self.nodes.next(head);
712 let last = self.cur_list().tail;
713 self.nodes.set_next(head, NodeRef::NULL);
714 self.pop_nest();
715 if self.mode == Mode::DisplayMath {
716 self.finish_display_alignment(first, last, pd_save);
717 return;
718 }
719 if self.mode.abs_kind() == ModeKind::Vertical {
720 self.cur_list_mut().prev_depth = pd_save;
721 } else if self.mode.abs_kind() == ModeKind::Horizontal {
722 self.cur_list_mut().space_factor = sf_save;
723 }
724 if !first.is_null() {
725 let tail = self.cur_list().tail;
726 self.nodes.link(tail, first);
727 self.cur_list_mut().tail = last;
728 }
729 if self.mode == Mode::Vertical {
730 self.build_page();
731 }
732 }
733
734 fn set_unset_row(&mut self, q: NodeRef, pb: &BoxData, o: i32, hmode: bool) {
736 let rb = match self.nodes.data(q) {
738 NodeData::Unset { b, .. } => (**b).clone(),
739 _ => return,
740 };
741 let mut nb = BoxData {
742 glue_order: pb.glue_order,
743 glue_sign: pb.glue_sign,
744 glue_set: pb.glue_set,
745 shift: o,
746 ..rb
747 };
748 if hmode {
749 nb.width = pb.width;
750 } else {
751 nb.height = pb.height;
752 }
753 let row_h = nb.height;
754 let row_d = nb.depth;
755 let row_w = nb.width;
756 {
757 let n = self.nodes.get_mut(q);
758 n.id = if hmode { id::HLIST } else { id::VLIST };
759 n.subtype = 4;
760 n.data = NodeData::Box(Box::new(nb));
761 }
762 let mut r = self.nodes.next(self.nodes.list_ptr(q));
763 let mut s = self.nodes.next(pb.head);
764 while !r.is_null() && !s.is_null() {
765 let (rbd, n, rst, rsto, rsh, rsho) = match self.nodes.data(r) {
767 NodeData::Unset {
768 b,
769 span_count,
770 stretch,
771 shrink,
772 stretch_order,
773 shrink_order,
774 } => (
775 (**b).clone(),
776 *span_count,
777 *stretch,
778 *stretch_order,
779 *shrink,
780 *shrink_order,
781 ),
782 _ => {
783 r = self.nodes.next(r);
785 if !r.is_null() {
786 r = self.nodes.next(r);
787 }
788 s = self.nodes.next(s);
789 if !s.is_null() {
790 s = self.nodes.next(s);
791 }
792 continue;
793 }
794 };
795 let sw = self.nodes.width(s);
796 let mut t = sw;
797 let w = t;
798 let hold = self.nodes.new_temp();
799 let mut u = hold;
800 for _ in 0..n {
801 s = self.nodes.next(s);
802 let v = self.nodes.glue_spec(s);
803 let g = self.new_glue_node(v, glue_sub::TAB_SKIP);
804 self.nodes.link(u, g);
805 u = g;
806 t += v.width;
807 if pb.glue_sign == 1 {
808 if v.stretch_order == pb.glue_order {
809 t += (pb.glue_set * f64::from(v.stretch)).round() as i32;
810 }
811 } else if pb.glue_sign == 2 && v.shrink_order == pb.glue_order {
812 t -= (pb.glue_set * f64::from(v.shrink)).round() as i32;
813 }
814 s = self.nodes.next(s);
815 let eb = self.nodes.new_null_box(hmode);
816 let swk = self.nodes.width(s);
817 if let Some(b) = self.nodes.box_data_mut(eb) {
818 if hmode {
819 b.width = swk;
820 } else {
821 b.height = swk;
822 }
823 }
824 self.nodes.link(u, eb);
825 u = eb;
826 t += swk;
827 }
828 let natural = if hmode { rbd.width } else { rbd.height };
829 let mut cb = BoxData { shift: 0, ..rbd };
830 if hmode {
831 cb.height = row_h;
832 cb.depth = row_d;
833 } else {
834 cb.width = row_w;
835 }
836 if t == natural {
837 cb.glue_sign = 0;
838 cb.glue_order = 0;
839 cb.glue_set = 0.0;
840 } else if t > natural {
841 cb.glue_sign = 1;
842 cb.glue_order = rsto;
843 cb.glue_set = if rst == 0 {
844 0.0
845 } else {
846 crate::nodes::glue_ratio(t - natural, rst)
847 };
848 } else {
849 cb.glue_order = rsho;
850 cb.glue_sign = 2;
851 cb.glue_set = if rsh == 0 {
852 0.0
853 } else if rsho == 0 && natural - t > rsh {
854 1.0
855 } else {
856 crate::nodes::glue_ratio(natural - t, rsh)
857 };
858 }
859 if hmode {
860 cb.width = w;
861 } else {
862 cb.height = w;
863 }
864 {
865 let nd = self.nodes.get_mut(r);
866 nd.id = if hmode { id::HLIST } else { id::VLIST };
867 nd.subtype = 5;
868 nd.data = NodeData::Box(Box::new(cb));
869 }
870 if u != hold {
871 let after = self.nodes.next(r);
872 let first = self.nodes.next(hold);
873 self.nodes.link(r, first);
874 self.nodes.link(u, after);
875 if after.is_null() {
876 self.nodes.set_next(u, NodeRef::NULL);
877 }
878 r = u;
879 }
880 self.nodes.free(hold);
881 r = self.nodes.next(r);
882 if !r.is_null() {
883 r = self.nodes.next(r);
884 }
885 s = self.nodes.next(s);
886 if !s.is_null() {
887 s = self.nodes.next(s);
888 }
889 }
890 }
891}