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.expand();
322 self.get_token();
323 }
324 }
325 if self.cur_cmd == Cmd::Endv {
326 self.fatal_error("(interwoven alignment preambles are not allowed)");
327 }
328 if self.cur_cmd == Cmd::AssignGlue && self.cur_chr == gp::TABSKIP {
329 self.scan_optional_equals();
330 self.scan_glue(crate::scan::ValLevel::Glue);
331 let g = self.cur_glue;
332 if self.eqtb.int(ip::GLOBALDEFS) > 0 {
333 self.geq_define(Loc::GluePar(gp::TABSKIP), Val::Glue(g));
334 } else {
335 self.eq_define(Loc::GluePar(gp::TABSKIP), Val::Glue(g));
336 }
337 continue;
338 }
339 return;
340 }
341 }
342
343 pub fn align_peek(&mut self) {
346 loop {
347 self.input.align_state = 1_000_000;
348 self.get_nonblank_or_protected();
349 if self.cur_cmd == Cmd::NoAlign {
350 self.scan_left_brace();
351 self.new_save_level(GroupCode::NoAlign);
352 if self.mode == Mode::InternalVertical {
353 self.normal_paragraph();
354 }
355 } else if self.cur_cmd == Cmd::RightBrace {
356 self.fin_align();
357 } else if self.cur_cmd == Cmd::CarRet && self.cur_chr == CR_CR_CODE {
358 continue;
359 } else {
360 self.init_row();
361 self.init_col();
362 }
363 return;
364 }
365 }
366
367 fn init_row(&mut self) {
369 let h = self.mode == Mode::InternalVertical;
370 self.push_nest(if h {
371 Mode::RestrictedHorizontal
372 } else {
373 Mode::InternalVertical
374 });
375 if h {
376 self.cur_list_mut().space_factor = 0;
377 } else {
378 self.cur_list_mut().prev_depth = 0;
379 }
380 let g = self.al().tabskips[0];
381 let p = self.new_glue_node(g, glue_sub::TAB_SKIP);
382 self.tail_append(p);
383 let l = self.al();
384 l.cur_align = Some(0);
385 l.adj_tail = l.adj_head;
386 self.init_span(0);
387 }
388
389 fn init_span(&mut self, p: usize) {
391 let h = self.mode == Mode::RestrictedHorizontal;
392 self.push_nest(if h {
393 Mode::RestrictedHorizontal
394 } else {
395 Mode::InternalVertical
396 });
397 if h {
398 self.cur_list_mut().space_factor = 1000;
399 } else {
400 self.cur_list_mut().prev_depth = IGNORE_DEPTH;
401 self.normal_paragraph();
402 }
403 self.al().cur_span = p;
404 }
405
406 fn init_col(&mut self) {
408 let cmd = self.cur_cmd;
409 let ca = self.al().cur_align.unwrap_or(0);
410 self.al().cols[ca].extra_info = cmd as u32;
411 if cmd == Cmd::Omit {
412 self.input.align_state = 0;
413 } else {
414 self.back_input();
415 let u = self.al().cols[ca].u.clone();
416 self.begin_token_list(u, TokKind::UTemplate);
417 }
418 }
419
420 pub fn insert_v_template(&mut self) {
422 let ca = self.al_ref().and_then(|l| l.cur_align);
423 let Some(ca) = ca else {
424 self.fatal_error("(interwoven alignment preambles are not allowed)");
425 };
426 if self.input.scanner_status == ScannerStatus::Aligning {
427 self.fatal_error("(interwoven alignment preambles are not allowed)");
428 }
429 let extra = self.al().cols[ca].extra_info;
430 self.al().cols[ca].extra_info = self.cur_chr;
431 if extra == Cmd::Omit as u32 {
432 let t: TokenList = Rc::from(vec![Token::cs(self.cs.frozen_end_template)]);
433 self.begin_token_list(t, TokKind::VTemplate);
434 } else {
435 let v = self.al().cols[ca].v.clone();
436 self.begin_token_list(v, TokKind::VTemplate);
437 }
438 self.input.align_state = 1_000_000;
439 }
440
441 pub fn do_endv(&mut self) {
443 let mut ok = false;
445 for lvl in self.input.stack.iter().rev() {
446 match lvl {
447 Level::Tokens(t) if t.kind != TokKind::VTemplate && t.loc >= t.list.len() => {
448 continue;
449 }
450 Level::Tokens(t) => {
451 ok = t.kind == TokKind::VTemplate && t.loc >= t.list.len();
452 break;
453 }
454 Level::Text(_) => break,
455 }
456 }
457 if !ok {
458 self.fatal_error("(interwoven alignment preambles are not allowed)");
459 }
460 if self.save.cur_group == GroupCode::Align {
461 self.end_graf();
462 if self.fin_col() {
463 self.fin_row();
464 }
465 } else {
466 self.off_save();
467 }
468 }
469
470 fn fin_col(&mut self) -> bool {
473 let Some(ca) = self.al_ref().and_then(|l| l.cur_align) else {
474 self.confusion("endv");
475 };
476 if self.input.align_state < 500_000 {
477 self.fatal_error("(interwoven alignment preambles are not allowed)");
478 }
479 let ncols = self.al().cols.len();
480 let mut p = ca + 1;
481 if p >= ncols && self.al().cols[ca].extra_info < CR_CODE {
484 if let Some(cl) = self.al().cur_loop {
485 let l = self.al();
487 let src = l.cols[cl].clone();
488 l.cols.push(Column {
489 u: src.u,
490 v: src.v,
491 width: NULL_FLAG,
492 spans: Vec::new(),
493 extra_info: 0,
494 });
495 let g = l.tabskips[cl + 1];
496 l.tabskips.push(g);
497 l.cur_loop = Some(cl + 1);
498 p = l.cols.len() - 1;
499 } else {
500 self.print_err("Extra alignment tab has been changed to ");
501 self.print_esc_str("cr");
502 self.help(&[
503 "You have given more \\span or & marks than there were",
504 "in the preamble to the \\halign or \\valign now in progress.",
505 "So I'll assume that you meant to type \\cr instead.",
506 ]);
507 self.al().cols[ca].extra_info = CR_CODE;
508 self.error();
509 }
510 }
511 if self.al().cols[ca].extra_info != SPAN_CODE {
512 self.unsave();
513 self.new_save_level(GroupCode::Align);
514 let head = self.cur_list().head;
516 let first = self.nodes.next(head);
517 self.nodes.set_next(head, NodeRef::NULL);
518 let h = self.mode == Mode::RestrictedHorizontal;
519 let (u, w) = if h {
520 let first = self.node_process_list(first, crate::nodeproc::Context::Hbox);
521 let first = self.filter_list("hpack_filter", first, "align_set");
522 let at = self.al().adj_tail;
523 self.ts.adjust_tail = Some(at);
524 self.ts.pre_adjust_tail = None;
525 let u = self.hpack(first, PackSpec::NATURAL);
526 let nt = self.ts.adjust_tail.take().unwrap_or(at);
527 self.al().adj_tail = nt;
528 let w = self.nodes.width(u);
529 (u, w)
530 } else {
531 let first = self.filter_list("vpack_filter", first, "align_set");
532 let u = self.vpackage(first, PackSpec::NATURAL, 0);
533 (u, self.node_height(u))
534 };
535 let cs = self.al().cur_span;
536 let mut n = 0usize;
537 if cs != ca {
538 n = ca - cs;
540 let col = &mut self.al().cols[cs];
541 match col.spans.iter_mut().find(|(k, _)| *k == n) {
542 Some(e) => {
543 if e.1 < w {
544 e.1 = w;
545 }
546 }
547 None => {
548 col.spans.push((n, w));
549 col.spans.sort_by_key(|e| e.0);
550 }
551 }
552 } else if w > self.al().cols[ca].width {
553 self.al().cols[ca].width = w;
554 }
555 let (st, sh) = self.ts.last_totals;
556 let so = (1..5).rev().find(|&o| st[o] != 0).unwrap_or(0);
557 let ho = (1..5).rev().find(|&o| sh[o] != 0).unwrap_or(0);
558 self.make_unset(u, n as u16, st[so], so as u8, sh[ho], ho as u8);
559 self.pop_nest();
560 self.tail_append(u);
561 let g = self.al().tabskips[ca + 1];
563 let gn = self.new_glue_node(g, glue_sub::TAB_SKIP);
564 self.tail_append(gn);
565 if self.al().cols[ca].extra_info >= CR_CODE {
566 return true;
567 }
568 self.init_span(p);
569 }
570 self.input.align_state = 1_000_000;
571 self.get_nonblank_or_protected();
572 self.al().cur_align = Some(p);
573 self.init_col();
574 false
575 }
576
577 fn make_unset(&mut self, u: NodeRef, span: u16, st: i32, so: u8, sh: i32, ho: u8) {
579 let b = match self.nodes.data(u) {
580 NodeData::Box(b) => (**b).clone(),
581 _ => BoxData::default(),
582 };
583 let n = self.nodes.get_mut(u);
584 n.id = id::UNSET;
585 n.subtype = 0;
586 n.data = NodeData::Unset {
587 b: Box::new(BoxData {
588 glue_set: 0.0,
589 glue_sign: 0,
590 glue_order: 0,
591 shift: 0,
592 ..b
593 }),
594 span_count: span,
595 stretch: st,
596 shrink: sh,
597 stretch_order: so,
598 shrink_order: ho,
599 };
600 }
601
602 fn fin_row(&mut self) {
604 let head = self.cur_list().head;
605 let first = self.nodes.next(head);
606 self.nodes.set_next(head, NodeRef::NULL);
607 let p = if self.mode == Mode::RestrictedHorizontal {
608 let first = self.filter_list("hpack_filter", first, "fin_row");
611 let p = self.hpack(first, PackSpec::NATURAL);
612 self.pop_nest();
613 self.append_to_vlist(p);
614 let (ah, at) = (self.al().adj_head, self.al().adj_tail);
615 if ah != at {
616 let f = self.nodes.next(ah);
617 let tail = self.cur_list().tail;
618 self.nodes.link(tail, f);
619 self.cur_list_mut().tail = at;
620 self.nodes.set_next(ah, NodeRef::NULL);
621 self.al().adj_tail = ah;
622 }
623 p
624 } else {
625 let first = self.filter_list("vpack_filter", first, "fin_row");
626 let p = self.vpack(first, PackSpec::NATURAL);
627 self.pop_nest();
628 self.tail_append(p);
629 self.cur_list_mut().space_factor = 1000;
630 p
631 };
632 self.make_unset(p, 0, 0, 0, 0, 0);
633 let t = self
634 .eqtb
635 .toks_param(crate::primitives_gen::assign_toks::EVERYCR)
636 .clone();
637 if !t.is_empty() {
638 self.begin_token_list(t, TokKind::EveryCrText);
639 }
640 self.align_peek();
641 }
642
643 fn fin_align(&mut self) {
645 if self.save.cur_group != GroupCode::Align {
646 self.confusion("align1");
647 }
648 self.unsave();
649 if self.save.cur_group != GroupCode::Align {
650 self.confusion("align0");
651 }
652 self.unsave();
653 let n = self.nest.len();
654 let o = if n >= 2 && self.nest[n - 2].mode == Mode::DisplayMath {
655 self.eqtb.dimen(dp::DISPLAYINDENT)
656 } else {
657 0
658 };
659 let ncols = self.al().cols.len();
661 let mut widths: Vec<i32> = self.al().cols.iter().map(|c| c.width).collect();
662 let mut spans: Vec<Vec<(usize, i32)>> =
663 self.al().cols.iter().map(|c| c.spans.clone()).collect();
664 let mut tabs: Vec<Glue> = self.al().tabskips.clone();
665 for q in 0..ncols {
666 if widths[q] == NULL_FLAG {
667 widths[q] = 0;
668 tabs[q + 1] = Glue::ZERO;
669 }
670 let mine = std::mem::take(&mut spans[q]);
671 if mine.is_empty() || q + 1 >= ncols {
672 continue;
673 }
674 let t = widths[q] + tabs[q + 1].width;
676 for (k, w) in mine {
677 let w = w - t;
678 if k <= 1 {
679 if w > widths[q + 1] {
680 widths[q + 1] = w;
681 }
682 } else {
683 match spans[q + 1].iter_mut().find(|(kk, _)| *kk == k - 1) {
684 Some(e) => {
685 if w > e.1 {
686 e.1 = w;
687 }
688 }
689 None => spans[q + 1].push((k - 1, w)),
690 }
691 }
692 }
693 }
694 let (spec, _dir) = {
696 let dir = self.pop_saved();
697 let v = self.pop_saved() as i32;
698 let code = self.pop_saved();
699 (
700 if code == 0 {
701 PackSpec::Exactly(v)
702 } else {
703 PackSpec::Additional(v)
704 },
705 dir,
706 )
707 };
708 let hmode = self.mode == Mode::InternalVertical;
709 let mut plist = NodeRef::NULL;
710 let mut ptail = NodeRef::NULL;
711 let mut push = |e: &mut Engine, n: NodeRef| {
712 if plist.is_null() {
713 plist = n;
714 } else {
715 e.nodes.link(ptail, n);
716 }
717 ptail = n;
718 };
719 for k in 0..=ncols {
720 let g = self.nodes.new_glue(tabs[k], glue_sub::TAB_SKIP);
721 push(self, g);
722 if k < ncols {
723 let u = self.nodes.new_null_box(true);
724 if let Some(b) = self.nodes.box_data_mut(u) {
725 if hmode {
726 b.width = widths[k];
727 } else {
728 b.height = widths[k];
729 }
730 }
731 self.make_unset(u, 0, 0, 0, 0, 0);
732 push(self, u);
733 }
734 }
735 self.ts.pack_begin_line = -self.cur_list().mode_line;
736 let p = if hmode {
737 let rule_save = self.eqtb.dimen_par[dp::OVERFULLRULE as usize].val;
738 self.eqtb.dimen_par[dp::OVERFULLRULE as usize].val = 0;
739 let p = self.hpack(plist, spec);
740 self.eqtb.dimen_par[dp::OVERFULLRULE as usize].val = rule_save;
741 p
742 } else {
743 let plist = self.filter_list("vpack_filter", plist, "preamble");
746 let p = self.vpack(plist, spec);
747 let mut q = self.nodes.next(self.nodes.list_ptr(p));
749 while !q.is_null() {
750 if let Some(b) = self.nodes.box_data_mut(q) {
751 b.width = b.height;
752 b.height = 0;
753 }
754 q = self.nodes.next(q);
755 if !q.is_null() {
756 q = self.nodes.next(q);
757 }
758 }
759 p
760 };
761 self.ts.pack_begin_line = 0;
762 let pb = self.nodes.box_data(p).cloned().unwrap_or_default();
763 let head = self.cur_list().head;
765 let mut s = head;
766 let mut q = self.nodes.next(head);
767 while !q.is_null() {
768 match self.nodes.id(q) {
769 id::UNSET => self.set_unset_row(q, &pb, o, hmode),
770 id::RULE => {
771 let (pw, ph, pd) = (pb.width, pb.height, pb.depth);
772 if let NodeData::Rule {
773 width,
774 height,
775 depth,
776 ..
777 } = self.nodes.data_mut(q)
778 {
779 if *width == NULL_FLAG {
780 *width = pw;
781 }
782 if *height == NULL_FLAG {
783 *height = ph;
784 }
785 if *depth == NULL_FLAG {
786 *depth = pd;
787 }
788 }
789 if o != 0 {
790 let r = self.nodes.next(q);
791 self.nodes.set_next(q, NodeRef::NULL);
792 let b = self.hpack(q, PackSpec::NATURAL);
793 if let Some(bd) = self.nodes.box_data_mut(b) {
794 bd.shift = o;
795 }
796 self.nodes.link(b, r);
797 self.nodes.link(s, b);
798 q = b;
799 }
800 }
801 _ => {}
802 }
803 s = q;
804 q = self.nodes.next(q);
805 }
806 self.nodes.flush_list(p);
807 self.pop_alignment();
808 let pd_save = self.cur_list().prev_depth;
810 let sf_save = self.cur_list().space_factor;
811 let head = self.cur_list().head;
812 let first = self.nodes.next(head);
813 let last = self.cur_list().tail;
814 self.nodes.set_next(head, NodeRef::NULL);
815 self.pop_nest();
816 if self.mode == Mode::DisplayMath {
817 self.finish_display_alignment(first, last, pd_save);
818 return;
819 }
820 if self.mode.abs_kind() == ModeKind::Vertical {
821 self.cur_list_mut().prev_depth = pd_save;
822 } else if self.mode.abs_kind() == ModeKind::Horizontal {
823 self.cur_list_mut().space_factor = sf_save;
824 }
825 if !first.is_null() {
826 let tail = self.cur_list().tail;
827 self.nodes.link(tail, first);
828 self.cur_list_mut().tail = last;
829 }
830 if self.mode == Mode::Vertical {
831 self.build_page_from("alignment");
832 }
833 }
834
835 fn set_unset_row(&mut self, q: NodeRef, pb: &BoxData, o: i32, hmode: bool) {
837 let first = self.nodes.next(self.nodes.list_ptr(q));
840 if first.is_null() || self.nodes.id(first) != id::UNSET {
841 self.luatex_fatal("alignment", "bad box");
842 }
843 let rb = match self.nodes.data(q) {
845 NodeData::Unset { b, .. } => (**b).clone(),
846 _ => return,
847 };
848 let mut nb = BoxData {
849 glue_order: pb.glue_order,
850 glue_sign: pb.glue_sign,
851 glue_set: pb.glue_set,
852 shift: o,
853 ..rb
854 };
855 if hmode {
856 nb.width = pb.width;
857 } else {
858 nb.height = pb.height;
859 }
860 let row_h = nb.height;
861 let row_d = nb.depth;
862 let row_w = nb.width;
863 {
864 let n = self.nodes.get_mut(q);
865 n.id = if hmode { id::HLIST } else { id::VLIST };
866 n.subtype = 4;
867 n.data = NodeData::Box(Box::new(nb));
868 }
869 let mut r = self.nodes.next(self.nodes.list_ptr(q));
870 let mut s = self.nodes.next(pb.head);
871 while !r.is_null() && !s.is_null() {
872 let (rbd, n, rst, rsto, rsh, rsho) = match self.nodes.data(r) {
874 NodeData::Unset {
875 b,
876 span_count,
877 stretch,
878 shrink,
879 stretch_order,
880 shrink_order,
881 } => (
882 (**b).clone(),
883 *span_count,
884 *stretch,
885 *stretch_order,
886 *shrink,
887 *shrink_order,
888 ),
889 _ => {
890 r = self.nodes.next(r);
892 if !r.is_null() {
893 r = self.nodes.next(r);
894 }
895 s = self.nodes.next(s);
896 if !s.is_null() {
897 s = self.nodes.next(s);
898 }
899 continue;
900 }
901 };
902 let sw = self.nodes.width(s);
903 let mut t = sw;
904 let w = t;
905 let hold = self.nodes.new_temp();
906 let mut u = hold;
907 for _ in 0..n {
908 s = self.nodes.next(s);
909 let v = self.nodes.glue_spec(s);
910 let g = self.new_glue_node(v, glue_sub::TAB_SKIP);
911 self.nodes.link(u, g);
912 u = g;
913 t += v.width;
914 if pb.glue_sign == 1 {
915 if v.stretch_order == pb.glue_order {
916 t += (pb.glue_set * f64::from(v.stretch)).round() as i32;
917 }
918 } else if pb.glue_sign == 2 && v.shrink_order == pb.glue_order {
919 t -= (pb.glue_set * f64::from(v.shrink)).round() as i32;
920 }
921 s = self.nodes.next(s);
922 let eb = self.nodes.new_null_box(hmode);
923 let swk = self.nodes.width(s);
924 if let Some(b) = self.nodes.box_data_mut(eb) {
925 if hmode {
926 b.width = swk;
927 } else {
928 b.height = swk;
929 }
930 }
931 self.nodes.link(u, eb);
932 u = eb;
933 t += swk;
934 }
935 let natural = if hmode { rbd.width } else { rbd.height };
936 let mut cb = BoxData { shift: 0, ..rbd };
937 if hmode {
938 cb.height = row_h;
939 cb.depth = row_d;
940 } else {
941 cb.width = row_w;
942 }
943 if t == natural {
944 cb.glue_sign = 0;
945 cb.glue_order = 0;
946 cb.glue_set = 0.0;
947 } else if t > natural {
948 cb.glue_sign = 1;
949 cb.glue_order = rsto;
950 cb.glue_set = if rst == 0 {
951 0.0
952 } else {
953 crate::nodes::glue_ratio(t - natural, rst)
954 };
955 } else {
956 cb.glue_order = rsho;
957 cb.glue_sign = 2;
958 cb.glue_set = if rsh == 0 {
959 0.0
960 } else if rsho == 0 && natural - t > rsh {
961 1.0
962 } else {
963 crate::nodes::glue_ratio(natural - t, rsh)
964 };
965 }
966 if hmode {
967 cb.width = w;
968 } else {
969 cb.height = w;
970 }
971 {
972 let nd = self.nodes.get_mut(r);
973 nd.id = if hmode { id::HLIST } else { id::VLIST };
974 nd.subtype = 5;
975 nd.data = NodeData::Box(Box::new(cb));
976 }
977 if u != hold {
978 let after = self.nodes.next(r);
979 let first = self.nodes.next(hold);
980 self.nodes.link(r, first);
981 self.nodes.link(u, after);
982 if after.is_null() {
983 self.nodes.set_next(u, NodeRef::NULL);
984 }
985 r = u;
986 }
987 self.nodes.free(hold);
988 r = self.nodes.next(r);
989 if !r.is_null() {
990 r = self.nodes.next(r);
991 }
992 s = self.nodes.next(s);
993 if !s.is_null() {
994 s = self.nodes.next(s);
995 }
996 }
997 }
998}