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