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