1use crate::Engine;
20use crate::build::PackSpec;
21use crate::fonts::LigOp;
22use crate::math::{Field, cramped_style, denom_style, half, num_style, sub_style, sup_style};
23use crate::mathparams::mp;
24use crate::nodes::{
25 DEFAULT_CODE, NodeData, NodeRef, fence_sub, glue_sub, id, kern_sub, noad_opt, noad_sub,
26 radical_sub,
27};
28use crate::primitives_gen::{assign_dimen as dp, assign_int as ip};
29
30pub const PROTECTED_GLYPH: u16 = 256;
32
33#[derive(Clone, Copy, Debug, PartialEq, Eq)]
35pub enum NoadType {
36 Ord = 0,
38 Op = 1,
40 Bin = 2,
42 Rel = 3,
44 Open = 4,
46 Close = 5,
48 Punct = 6,
50 Inner = 7,
52 Radical,
54 Fraction,
56 Under,
58 Over,
60 Accent,
62 Vcenter,
64 Left,
66 Right,
68}
69
70const MATH_SPACING: &[u8; 64] = b"0234000122*4000133**3**344*0400400*000000234000111*1111112341011";
73
74impl Engine {
75 pub fn is_opentype_math_font(&self, f: u32) -> bool {
78 self.font(f).math_constants.is_some()
79 }
80
81 pub fn math_spacing(&self, l: u32, r: u32, style: u32) -> Option<(crate::glue::Glue, u16)> {
86 let p = 49 + l * 8 + r;
87 let v = self.math_param_value(p, style);
88 if v != crate::codes::UNDEFINED_MATH_PARAM {
89 return Some((self.math_param_glue(v), 0));
90 }
91 let c = MATH_SPACING[(l * 8 + r) as usize];
92 let x = match c {
93 b'1' if style < 4 => glue_sub::THIN_MU_SKIP,
94 b'2' => glue_sub::THIN_MU_SKIP,
95 b'3' if style < 4 => glue_sub::MED_MU_SKIP,
96 b'4' if style < 4 => glue_sub::THICK_MU_SKIP,
97 _ => return None,
98 };
99 let par = match x {
100 glue_sub::THIN_MU_SKIP => crate::primitives_gen::assign_mu_glue::THINMUSKIP,
101 glue_sub::MED_MU_SKIP => crate::primitives_gen::assign_mu_glue::MEDMUSKIP,
102 _ => crate::primitives_gen::assign_mu_glue::THICKMUSKIP,
103 };
104 Some((self.eqtb.glue(par + crate::eqtb::MU_GLUE_OFFSET), x))
105 }
106}
107
108#[derive(Clone, Copy, Debug, Default)]
110pub struct MathState {
111 pub style: i32,
113 pub size: u32,
115 pub mu: i32,
117 pub op_kern: i32,
120}
121
122pub fn style_size(s: i32) -> u32 {
124 if s < 4 { 0 } else { ((s - 2) / 2) as u32 }
125}
126
127impl Engine {
128 pub fn noad_type(&self, q: NodeRef) -> Option<NoadType> {
130 Some(match self.nodes.id(q) {
131 id::NOAD => match self.nodes.subtype(q) {
132 noad_sub::ORD => NoadType::Ord,
133 noad_sub::OP_DISPLAY_LIMITS | noad_sub::OP_LIMITS | noad_sub::OP_NO_LIMITS => {
134 NoadType::Op
135 }
136 noad_sub::BIN => NoadType::Bin,
137 noad_sub::REL => NoadType::Rel,
138 noad_sub::OPEN => NoadType::Open,
139 noad_sub::CLOSE => NoadType::Close,
140 noad_sub::PUNCT => NoadType::Punct,
141 noad_sub::INNER => NoadType::Inner,
142 noad_sub::UNDER => NoadType::Under,
143 noad_sub::OVER => NoadType::Over,
144 _ => NoadType::Vcenter,
145 },
146 id::RADICAL => NoadType::Radical,
147 id::ACCENT => NoadType::Accent,
148 id::FRACTION => NoadType::Fraction,
149 id::FENCE => {
150 if self.nodes.subtype(q) == fence_sub::LEFT {
151 NoadType::Left
152 } else {
153 NoadType::Right
154 }
155 }
156 _ => return None,
157 })
158 }
159
160 fn set_cur_style(&mut self, s: i32) {
162 self.ts.math.style = s;
163 self.ts.math.size = style_size(s);
164 let q = self.math_par(mp::QUAD);
165 self.ts.math.mu = retex_arith::x_over_n(q, 18).map_or(0, |r| r.quotient);
166 }
167
168 pub fn math_par(&self, p: u32) -> i32 {
170 let v = self.math_param_value(p, self.ts.math.style as u32);
171 if v == crate::codes::UNDEFINED_MATH_PARAM {
172 0
173 } else {
174 v
175 }
176 }
177
178 fn math_par_opt(&self, p: u32) -> Option<i32> {
180 let v = self.math_param_value(p, self.ts.math.style as u32);
181 (v != crate::codes::UNDEFINED_MATH_PARAM).then_some(v)
182 }
183
184 pub fn mlist_to_hlist(&mut self, mlist: NodeRef, style: i32, penalties: bool) -> NodeRef {
188 self.fonts.sync_dense();
189 self.init_math_params_once();
190 let saved = self.ts.math;
191 let r = self.mlist_to_hlist_rec(mlist, style, penalties);
192 self.ts.math = saved;
193 r
194 }
195
196 fn get_new_hlist(&self, q: NodeRef) -> NodeRef {
197 match self.nodes.data(q) {
198 NodeData::Noad(n) => n.new_hlist,
199 NodeData::Fraction(f) => f.new_hlist,
200 NodeData::Fence(f) => f.new_hlist,
201 _ => NodeRef::NULL,
202 }
203 }
204
205 fn set_new_hlist(&mut self, q: NodeRef, p: NodeRef) {
206 match self.nodes.data_mut(q) {
207 NodeData::Noad(n) => n.new_hlist = p,
208 NodeData::Fraction(f) => f.new_hlist = p,
209 NodeData::Fence(f) => f.new_hlist = p,
210 _ => {}
211 }
212 }
213
214 fn mlist_to_hlist_rec(&mut self, mlist: NodeRef, style: i32, penalties: bool) -> NodeRef {
215 let mut q = mlist;
216 let mut r = NodeRef::NULL;
217 let mut r_type = NoadType::Op;
218 let mut max_h = 0i32;
219 let mut max_d = 0i32;
220 self.set_cur_style(style);
221 while !q.is_null() {
222 let mut delta = 0;
224 let Some(mut t) = self.noad_type(q) else {
225 let next = self.first_pass_node(q, &mut max_h, &mut max_d);
227 q = next;
228 continue;
229 };
230 let mut action = Action::Convert;
231 loop {
232 match t {
233 NoadType::Bin => {
234 if matches!(
235 r_type,
236 NoadType::Bin
237 | NoadType::Op
238 | NoadType::Rel
239 | NoadType::Open
240 | NoadType::Punct
241 | NoadType::Left
242 ) {
243 self.nodes.set_subtype(q, noad_sub::ORD);
244 t = NoadType::Ord;
245 continue;
246 }
247 }
248 NoadType::Rel | NoadType::Close | NoadType::Punct | NoadType::Right => {
249 if r_type == NoadType::Bin {
250 self.nodes.set_subtype(r, noad_sub::ORD);
251 }
252 if t == NoadType::Right {
253 action = Action::DoneWithNoad;
254 }
255 }
256 NoadType::Left => action = Action::DoneWithNoad,
257 NoadType::Fraction => {
258 self.make_fraction(q);
259 action = Action::CheckDimensions;
260 }
261 NoadType::Op => {
262 delta = self.make_op(q);
263 if self.nodes.subtype(q) == noad_sub::OP_LIMITS {
264 action = Action::CheckDimensions;
265 }
266 }
267 NoadType::Ord => self.make_ord(q),
268 NoadType::Open | NoadType::Inner => {}
269 NoadType::Radical => self.make_radical(q),
270 NoadType::Over => self.make_over(q),
271 NoadType::Under => self.make_under(q),
272 NoadType::Accent => self.make_math_accent(q),
273 NoadType::Vcenter => self.make_vcenter(q),
274 }
275 break;
276 }
277 if action == Action::Convert {
278 self.convert_nucleus(q, delta);
279 }
280 if action != Action::DoneWithNoad {
281 let nh = self.get_new_hlist(q);
283 let (h, d) = self.list_height_depth(nh);
284 if h > max_h {
285 max_h = h;
286 }
287 if d > max_d {
288 max_d = d;
289 }
290 }
291 r = q;
293 r_type = self.noad_type(r).unwrap_or(NoadType::Ord);
294 if r_type == NoadType::Right {
295 r_type = NoadType::Left;
296 self.set_cur_style(style);
297 }
298 q = self.nodes.next(q);
299 }
300 if r_type == NoadType::Bin {
301 self.nodes.set_subtype(r, noad_sub::ORD);
302 }
303 self.second_pass(mlist, style, penalties, max_h, max_d)
304 }
305
306 fn list_height_depth(&mut self, p: NodeRef) -> (i32, i32) {
308 let saved = self.ts.adjust_tail.take();
309 let pre = self.ts.pre_adjust_tail.take();
310 let z = self.hpack_quiet(p);
311 self.ts.adjust_tail = saved;
312 self.ts.pre_adjust_tail = pre;
313 let (h, d) = self
314 .nodes
315 .box_data(z)
316 .map_or((0, 0), |b| (b.height, b.depth));
317 self.nodes.set_list_ptr(z, NodeRef::NULL);
318 self.nodes.free(z);
319 (h, d)
320 }
321
322 fn hpack_quiet(&mut self, p: NodeRef) -> NodeRef {
325 self.hpack(p, PackSpec::NATURAL)
326 }
327
328 fn first_pass_node(&mut self, q: NodeRef, max_h: &mut i32, max_d: &mut i32) -> NodeRef {
330 match self.nodes.data(q) {
331 NodeData::Style { style } => {
332 let s = i32::from(*style);
333 self.set_cur_style(s);
334 }
335 NodeData::Choice(c) => {
336 let c = **c;
337 let k = (self.ts.math.style / 2) as usize;
338 let p = c[k.min(3)];
339 for (i, l) in c.iter().enumerate() {
340 if i != k.min(3) {
341 self.nodes.flush_list(*l);
342 }
343 }
344 let st = self.ts.math.style as u8;
345 *self.nodes.get_mut(q) = crate::nodes::Node {
346 data: NodeData::Style { style: st },
347 id: id::STYLE,
348 ..self.nodes.get(q).clone()
349 };
350 if !p.is_null() {
351 let z = self.nodes.next(q);
352 self.nodes.link(q, p);
353 let t = self.nodes.tail_of(p);
354 self.nodes.link(t, z);
355 }
356 }
357 NodeData::Rule { height, depth, .. } => {
358 if *height > *max_h {
359 *max_h = *height;
360 }
361 if *depth > *max_d {
362 *max_d = *depth;
363 }
364 }
365 NodeData::Glue { spec, .. } => {
366 let spec = *spec;
367 let sub = self.nodes.subtype(q);
368 if sub == glue_sub::MU_GLUE {
369 let g = self.math_glue(spec, self.ts.math.mu);
370 if let NodeData::Glue { spec, .. } = self.nodes.data_mut(q) {
371 *spec = g;
372 }
373 self.nodes.set_subtype(q, glue_sub::USER);
374 } else if self.ts.math.size != 0 && sub == glue_sub::COND_MATH_GLUE {
375 let p = self.nodes.next(q);
376 if !p.is_null() && matches!(self.nodes.id(p), id::GLUE | id::KERN) {
377 let n = self.nodes.next(p);
378 self.nodes.link(q, n);
379 if n.is_null() {
380 self.nodes.set_next(q, NodeRef::NULL);
381 }
382 self.nodes.set_next(p, NodeRef::NULL);
383 self.nodes.flush_list(p);
384 }
385 }
386 }
387 NodeData::Kern { kern, .. } => {
388 if self.nodes.subtype(q) == glue_sub::MU_GLUE {
389 let w = self.mu_mult(*kern, self.ts.math.mu);
390 if let NodeData::Kern { kern, .. } = self.nodes.data_mut(q) {
391 *kern = w;
392 }
393 self.nodes.set_subtype(q, kern_sub::ITALIC);
395 }
396 }
397 _ => {}
398 }
399 self.nodes.next(q)
400 }
401
402 fn mu_mult(&self, x: i32, m: i32) -> i32 {
404 let r = retex_arith::x_over_n_unchecked(m, 0o200000);
405 let (mut n, mut f) = (r.quotient, r.remainder);
406 if f < 0 {
407 n -= 1;
408 f += 0o200000;
409 }
410 let frac = retex_arith::xn_over_d(x, f, 0o200000).map_or(0, |r| r.quotient);
411 retex_arith::nx_plus_y(n, x, frac).unwrap_or(0)
412 }
413
414 pub fn math_glue(&self, g: crate::glue::Glue, m: i32) -> crate::glue::Glue {
416 let mut r = g;
417 r.width = self.mu_mult(g.width, m);
418 if g.stretch_order == 0 {
419 r.stretch = self.mu_mult(g.stretch, m);
420 }
421 if g.shrink_order == 0 {
422 r.shrink = self.mu_mult(g.shrink, m);
423 }
424 r
425 }
426
427 fn fetch(&mut self, k: NodeRef) -> Option<(u32, u32, crate::fonts::CharMetrics)> {
432 let (fam, chr) = match self.nodes.data(k) {
433 NodeData::MathChar { fam, chr } => (*fam, *chr),
434 _ => return None,
435 };
436 let size = self.ts.math.size;
437 let f = self.fam_fnt(fam, size);
438 if f == 0 {
439 self.print_err("");
440 self.print_size(size);
441 self.printer.print_int(i64::from(fam));
442 self.printer.print_str(" is undefined (character ");
443 self.printer.print_int(i64::from(chr));
444 self.printer.print_char(')' as u32);
445 self.help(&[
446 "Somewhere in the math formula just ended, you used the",
447 "stated character from an undefined font family. For example,",
448 "plain TeX doesn't allow \\it or \\sl in subscripts. Proceed,",
449 "and I'll try to forget that I needed that character.",
450 ]);
451 self.error();
452 return None;
453 }
454 match self.fonts.char_metrics(f, chr) {
455 Some(m) => Some((f, chr, *m)),
456 None => {
457 self.char_warning(f, chr);
458 None
459 }
460 }
461 }
462
463 pub fn print_size(&mut self, s: u32) {
466 self.print_esc_str(match s {
467 0 => "textfont",
468 1 => "scriptfont",
469 _ => "scriptscriptfont",
470 });
471 }
472
473 pub fn char_warning(&mut self, f: u32, c: u32) {
476 if self.eqtb.int(ip::TRACINGLOSTCHARS) > 0 {
477 let old = self.eqtb.int(ip::TRACINGONLINE);
478 self.begin_diagnostic();
479 self.printer.print_nl("Missing character: There is no ");
480 self.printer.print_char(c);
481 self.printer.print_str(" in font ");
482 let name = self.font(f).name.clone();
483 self.printer.print_bytes(&name);
484 self.printer.print_char('!' as u32);
485 self.end_diagnostic(false);
486 let _ = old;
487 }
488 }
489
490 fn new_math_glyph(&mut self, f: u32, c: u32) -> NodeRef {
492 let g = self.new_char_node(f, c);
493 self.nodes.set_subtype(g, PROTECTED_GLYPH);
494 g
495 }
496
497 fn char_box(&mut self, f: u32, c: u32) -> NodeRef {
500 let m = self.fonts.char_metrics(f, c).copied().unwrap_or_default();
501 let italic = if self.is_opentype_math_font(f) {
503 0
504 } else {
505 m.italic
506 };
507 let b = self.new_null_box_node(true);
508 let g = self.new_math_glyph(f, c);
509 if let Some(bd) = self.nodes.box_data_mut(b) {
510 bd.width = m.width + italic;
511 bd.height = m.height;
512 bd.depth = m.depth;
513 bd.head = g;
514 }
515 b
516 }
517
518 fn char_tag(&self, f: u32, c: u32) -> (u8, u32) {
521 if let Some(t) = self.tfm_of(f)
522 && let Some(ci) = t.char_info(c)
523 {
524 return (ci.tag, u32::from(ci.remainder));
525 }
526 if let Some(m) = self.math_char(f, c) {
527 if let Some(n) = m.next {
528 return (2, n);
529 }
530 if m.vert.is_some() {
531 return (3, c);
532 }
533 }
534 (0, 0)
535 }
536
537 fn clean_box(&mut self, k: NodeRef, s: i32) -> NodeRef {
541 let mut q;
542 let kid = if k.is_null() { 0 } else { self.nodes.id(k) };
543 match kid {
544 id::MATH_CHAR | id::MATH_TEXT_CHAR => {
545 let n = self.new_noad(noad_sub::ORD);
546 let kc = self.nodes.copy_node(k);
547 self.set_noad_field(n, Field::Nucleus, kc);
548 q = self.clean_box_mlist(n, s);
549 }
550 id::SUB_BOX => {
551 q = self.nodes.list_ptr_kernel(k);
552 self.nodes.set_kernel_list(k, NodeRef::NULL);
553 }
554 id::SUB_MLIST => {
555 let l = self.nodes.list_ptr_kernel(k);
556 self.nodes.set_kernel_list(k, NodeRef::NULL);
557 q = self.clean_box_mlist(l, s);
558 }
559 _ => {
560 q = self.new_null_box_node(true);
561 }
562 }
563 let x = if q.is_null() || self.nodes.is_glyph(q) {
565 self.hpack_quiet(q)
566 } else if self.nodes.next(q).is_null()
567 && matches!(self.nodes.id(q), id::HLIST | id::VLIST)
568 && self.nodes.box_data(q).is_some_and(|b| b.shift == 0)
569 {
570 q
571 } else {
572 self.hpack_quiet(q)
573 };
574 q = self.nodes.list_ptr(x);
576 if !q.is_null() && self.nodes.is_glyph(q) {
577 let r = self.nodes.next(q);
578 if !r.is_null() && self.nodes.next(r).is_null() && self.nodes.id(r) == id::KERN {
579 self.nodes.free(r);
580 self.nodes.set_next(q, NodeRef::NULL);
581 }
582 }
583 x
584 }
585
586 fn clean_box_mlist(&mut self, l: NodeRef, s: i32) -> NodeRef {
587 let saved = self.ts.math;
588 let r = self.mlist_to_hlist_rec(l, s, false);
589 self.ts.math = saved;
590 r
591 }
592
593 fn rebox(&mut self, b: NodeRef, w: i32) -> NodeRef {
595 let bw = self.nodes.box_data(b).map_or(0, |x| x.width);
596 let lp = self.nodes.list_ptr(b);
597 if bw != w && !lp.is_null() {
598 let mut b = b;
599 if self.nodes.id(b) == id::VLIST {
600 b = self.hpack_quiet(b);
601 }
602 let mut p = self.nodes.list_ptr(b);
603 let bw = self.nodes.box_data(b).map_or(0, |x| x.width);
604 if self.nodes.is_glyph(p) && self.nodes.next(p).is_null() {
605 let (f, c) = {
606 let g = self.nodes.glyph(p).expect("glyph");
607 (g.font, g.chr)
608 };
609 let v = self.fonts.char_metrics(f, c).map_or(0, |m| m.width);
610 if v != bw {
611 let k = self.new_kern_node(bw - v, kern_sub::FONT);
612 self.nodes.link(p, k);
613 }
614 }
615 self.nodes.set_list_ptr(b, NodeRef::NULL);
616 self.nodes.free(b);
617 let ss = crate::glue::Glue {
618 stretch: retex_arith::UNITY,
619 stretch_order: crate::glue::FIL,
620 shrink: retex_arith::UNITY,
621 shrink_order: crate::glue::FIL,
622 ..crate::glue::Glue::ZERO
623 };
624 let g1 = self.new_glue_node(ss, glue_sub::USER);
625 self.nodes.link(g1, p);
626 while !self.nodes.next(p).is_null() {
627 p = self.nodes.next(p);
628 }
629 let g2 = self.new_glue_node(ss, glue_sub::USER);
630 self.nodes.link(p, g2);
631 self.hpack(g1, PackSpec::Exactly(w))
632 } else {
633 if let Some(x) = self.nodes.box_data_mut(b) {
634 x.width = w;
635 }
636 b
637 }
638 }
639
640 fn fraction_rule(&mut self, t: i32) -> NodeRef {
642 self.new_node(
643 id::RULE,
644 0,
645 NodeData::Rule {
646 width: crate::typeset::NULL_FLAG,
647 depth: 0,
648 height: t,
649 dir: 0,
650 index: 0,
651 left: 0,
652 right: 0,
653 },
654 )
655 }
656
657 fn overbar(&mut self, b: NodeRef, k: i32, t: i32, top: i32) -> NodeRef {
659 let p = self.new_kern_node(k, kern_sub::FONT);
660 self.nodes.link(p, b);
661 let q = self.fraction_rule(t);
662 self.nodes.link(q, p);
663 let p = self.new_kern_node(top, kern_sub::FONT);
664 self.nodes.link(p, q);
665 self.vpack(p, PackSpec::NATURAL)
666 }
667
668 pub fn var_delimiter(&mut self, d: NodeRef, s: u32, v: i32, shift: bool) -> NodeRef {
674 let (sf, sc, lf, lc) = self.delim_fields(d);
675 let mut best: Option<(u32, u32, bool)> = None;
676 let mut w = 0i32;
677 'search: for (z0, x0) in [(sf, sc), (lf, lc)] {
678 if z0 == 0 && x0 == 0 {
679 continue;
680 }
681 let mut z = s as i32;
682 while z >= 0 {
683 let g = self.fam_fnt(z0, z as u32);
684 if g != 0 {
685 let mut y = x0;
686 let mut guard = 0;
687 loop {
688 guard += 1;
689 if guard > 256 {
690 break;
691 }
692 let Some(m) = self.fonts.char_metrics(g, y).copied() else {
693 break;
694 };
695 let (tag, rem) = self.char_tag(g, y);
696 let opentype = self.is_opentype_math_font(g);
699 if tag == 3 && !opentype {
700 best = Some((g, y, true));
701 break 'search;
702 }
703 let u = m.height + m.depth;
704 if u > w {
705 best = Some((g, y, false));
706 w = u;
707 if u >= v {
708 break 'search;
709 }
710 }
711 if opentype && self.vert_parts(g, y).is_some() {
712 best = Some((g, y, true));
713 break 'search;
714 }
715 if tag == 2 {
716 y = rem;
717 } else {
718 break;
719 }
720 }
721 }
722 z -= 1;
723 }
724 }
725 let b = match best {
726 Some((f, c, true)) if self.is_opentype_math_font(f) => {
727 let saved = self.ts.math;
728 self.set_cur_style(if s == 0 { 2 } else { 2 * s as i32 + 2 });
729 let overlap = self.math_par(mp::CONNECTOR_OVERLAP_MIN);
730 self.ts.math = saved;
731 self.ot_extensible_box(f, c, v, overlap)
732 }
733 Some((f, c, true)) => self.extensible_box(f, c, v),
734 Some((f, c, false)) => self.char_box(f, c),
735 None => {
736 let b = self.new_null_box_node(true);
737 let nds = self.eqtb.dimen(dp::NULLDELIMITERSPACE);
738 if let Some(bd) = self.nodes.box_data_mut(b) {
739 bd.width = nds;
740 }
741 if !shift {
742 return b;
743 }
744 b
745 }
746 };
747 let saved = self.ts.math;
748 self.set_cur_style(if s == 0 { 2 } else { 2 * s as i32 + 2 });
749 let axis = self.math_par(mp::AXIS);
750 self.ts.math = saved;
751 if let Some(bd) = self.nodes.box_data_mut(b) {
752 bd.shift = half(bd.height - bd.depth) - axis;
753 }
754 b
755 }
756
757 fn nested_accent_char(&mut self, k: NodeRef) -> Option<(u32, u32)> {
760 if k.is_null() || self.nodes.id(k) != id::SUB_MLIST {
761 return None;
762 }
763 let l = self.nodes.list_ptr_kernel(k);
764 if l.is_null()
765 || !self.nodes.next(l).is_null()
766 || self.noad_type(l) != Some(NoadType::Accent)
767 {
768 return None;
769 }
770 let n = self.noad_field(l, Field::Nucleus);
771 if n.is_null() {
772 return None;
773 }
774 if self.nodes.id(n) == id::MATH_CHAR {
775 return self.fetch(n).map(|(f, c, _)| (f, c));
776 }
777 self.nested_accent_char(n)
778 }
779
780 fn vert_parts(&self, f: u32, c: u32) -> Option<std::rc::Rc<[crate::fonts::ExtPart]>> {
782 self.math_char(f, c)?.vert.clone()
783 }
784
785 fn ot_extensible_box(&mut self, f: u32, c: u32, v: i32, overlap: i32) -> NodeRef {
793 let parts = self
794 .vert_parts(f, c)
795 .unwrap_or_else(|| std::rc::Rc::from(Vec::new()));
796 let expand = |n: usize| -> Vec<crate::fonts::ExtPart> {
797 let mut out = Vec::new();
798 for p in parts.iter() {
799 let k = if p.extender { n } else { 1 };
800 for _ in 0..k {
801 out.push(*p);
802 }
803 }
804 out
805 };
806 let joint = |a: &crate::fonts::ExtPart, b: &crate::fonts::ExtPart| {
807 let max = a.end.min(b.start).max(0);
808 (max, overlap.min(max))
809 };
810 let largest = |seq: &[crate::fonts::ExtPart]| -> i64 {
811 let mut t: i64 = seq.iter().map(|p| i64::from(p.advance)).sum();
812 for w in seq.windows(2) {
813 t -= i64::from(joint(&w[0], &w[1]).1);
814 }
815 t
816 };
817 let has_extender = parts.iter().any(|p| p.extender);
818 let mut n = 0;
819 let mut seq = expand(n);
820 while has_extender && largest(&seq) < i64::from(v) && n < 1000 {
821 n += 1;
822 seq = expand(n);
823 }
824 let mut head = NodeRef::NULL;
826 let mut tail = NodeRef::NULL;
827 let mut width = 0;
828 for (i, p) in seq.iter().enumerate().rev() {
829 if i + 1 < seq.len() {
830 let (max, min) = joint(p, &seq[i + 1]);
831 let g = crate::glue::Glue {
832 width: -max,
833 stretch: max - min,
834 ..crate::glue::Glue::ZERO
835 };
836 let gn = self.new_glue_node(g, 0);
837 self.nodes.link(tail, gn);
838 tail = gn;
839 }
840 let m = self
841 .fonts
842 .char_metrics(f, p.code)
843 .copied()
844 .unwrap_or_default();
845 let b = self.new_null_box_node(true);
846 let gl = self.new_math_glyph(f, p.code);
847 if let Some(bd) = self.nodes.box_data_mut(b) {
848 bd.width = m.width;
849 bd.height = p.advance;
850 bd.depth = 0;
851 bd.head = gl;
852 }
853 width = width.max(m.width);
854 if head.is_null() {
855 head = b;
856 } else {
857 self.nodes.link(tail, b);
858 }
859 tail = b;
860 }
861 let natural = largest(&seq) as i32;
862 let target = v.max(natural.min(v));
863 let b = self.vpack(head, crate::build::PackSpec::Exactly(target));
864 if let Some(bd) = self.nodes.box_data_mut(b) {
865 bd.width = width;
866 }
867 b
868 }
869
870 fn extensible_box(&mut self, f: u32, c: u32, v: i32) -> NodeRef {
873 let recipe = self
874 .tfm_of(f)
875 .and_then(|t| {
876 let (_, rem) = self.char_tag(f, c);
877 t.exten.get(rem as usize).copied()
878 })
879 .unwrap_or([0, 0, 0, 0]);
880 let [top, mid, bot, rep] = recipe.map(u32::from);
881 let hd = |e: &Engine, ch: u32| {
882 e.fonts
883 .char_metrics(f, ch)
884 .map_or(0, |m| m.height + m.depth)
885 };
886 let b = self.new_null_box_node(false);
887 let u = hd(self, rep);
888 let mut w = 0;
889 let rm = self.fonts.char_metrics(f, rep).copied().unwrap_or_default();
890 let width = rm.width + rm.italic;
891 if bot != 0 {
892 w += hd(self, bot);
893 }
894 if mid != 0 {
895 w += hd(self, mid);
896 }
897 if top != 0 {
898 w += hd(self, top);
899 }
900 let mut n = 0;
901 if u > 0 {
902 while w < v {
903 w += u;
904 n += 1;
905 if mid != 0 {
906 w += u;
907 }
908 }
909 }
910 if let Some(bd) = self.nodes.box_data_mut(b) {
911 bd.width = width;
912 }
913 if bot != 0 {
914 self.stack_into_box(b, f, bot);
915 }
916 for _ in 0..n {
917 self.stack_into_box(b, f, rep);
918 }
919 if mid != 0 {
920 self.stack_into_box(b, f, mid);
921 for _ in 0..n {
922 self.stack_into_box(b, f, rep);
923 }
924 }
925 if top != 0 {
926 self.stack_into_box(b, f, top);
927 }
928 if let Some(bd) = self.nodes.box_data_mut(b) {
931 bd.height = w;
932 bd.depth = 0;
933 }
934 b
935 }
936
937 fn stack_into_box(&mut self, b: NodeRef, f: u32, c: u32) {
938 let p = self.char_box(f, c);
939 let head = self.nodes.list_ptr(b);
940 self.nodes.link(p, head);
941 self.nodes.set_list_ptr(b, p);
942 let h = self.nodes.box_data(p).map_or(0, |x| x.height);
943 if let Some(bd) = self.nodes.box_data_mut(b) {
944 bd.height = h;
945 }
946 }
947
948 fn take_field(&mut self, q: NodeRef, f: Field) -> NodeRef {
951 let k = self.noad_field(q, f);
952 self.set_noad_field(q, f, NodeRef::NULL);
953 k
954 }
955
956 fn set_nucleus_box(&mut self, q: NodeRef, b: NodeRef) {
958 let old = self.take_field(q, Field::Nucleus);
959 self.nodes.flush_list(old);
960 let k = self.new_sub_list(false, b);
961 self.set_noad_field(q, Field::Nucleus, k);
962 }
963
964 fn make_over(&mut self, q: NodeRef) {
965 let k = self.noad_field(q, Field::Nucleus);
966 let st = cramped_style(self.ts.math.style);
967 let x = self.clean_box(k, st);
968 let b = self.overbar(
969 x,
970 self.math_par(mp::OVERBAR_VGAP),
971 self.math_par(mp::OVERBAR_RULE),
972 self.math_par(mp::OVERBAR_KERN),
973 );
974 self.set_nucleus_box(q, b);
975 }
976
977 fn make_under(&mut self, q: NodeRef) {
978 let k = self.noad_field(q, Field::Nucleus);
979 let x = self.clean_box(k, self.ts.math.style);
980 let p = self.new_kern_node(self.math_par(mp::UNDERBAR_VGAP), kern_sub::FONT);
981 self.nodes.link(x, p);
982 let r = self.fraction_rule(self.math_par(mp::UNDERBAR_RULE));
983 self.nodes.link(p, r);
984 let y = self.vpack(x, PackSpec::NATURAL);
985 let (yh, yd) = self
986 .nodes
987 .box_data(y)
988 .map_or((0, 0), |b| (b.height, b.depth));
989 let xh = self.nodes.box_data(x).map_or(0, |b| b.height);
990 let delta = yh + yd + self.math_par(mp::UNDERBAR_KERN);
991 if let Some(b) = self.nodes.box_data_mut(y) {
992 b.height = xh;
993 b.depth = delta - xh;
994 }
995 self.set_nucleus_box(q, y);
996 }
997
998 fn make_vcenter(&mut self, q: NodeRef) {
999 let k = self.noad_field(q, Field::Nucleus);
1000 let v = self.nodes.list_ptr_kernel(k);
1001 if v.is_null() || self.nodes.id(v) != id::VLIST {
1002 self.confusion("vcenter");
1003 }
1004 let axis = self.math_par(mp::AXIS);
1005 if let Some(b) = self.nodes.box_data_mut(v) {
1006 let delta = b.height + b.depth;
1007 b.height = axis + half(delta);
1008 b.depth = delta - b.height;
1009 }
1010 }
1011
1012 fn make_radical(&mut self, q: NodeRef) {
1013 let sub = self.nodes.subtype(q);
1014 match sub {
1015 radical_sub::UUNDERDELIMITER
1016 | radical_sub::UOVERDELIMITER
1017 | radical_sub::UDELIMITERUNDER
1018 | radical_sub::UDELIMITEROVER
1019 | radical_sub::UHEXTENSIBLE => {
1020 self.make_delimiter_radical(q, sub);
1021 return;
1022 }
1023 _ => {}
1024 }
1025 let k = self.noad_field(q, Field::Nucleus);
1026 let x = self.clean_box(k, cramped_style(self.ts.math.style));
1027 let (xh, xd) = self
1028 .nodes
1029 .box_data(x)
1030 .map_or((0, 0), |b| (b.height, b.depth));
1031 let mut clr = self.math_par(mp::RADICAL_VGAP);
1032 let theta = self.math_par_opt(mp::RADICAL_RULE);
1033 let kern = self.math_par(mp::RADICAL_KERN);
1034 let t = theta.unwrap_or(self.math_par(mp::FRACTION_RULE));
1035 let left = self.nodes.noad(q).map_or(NodeRef::NULL, |n| n.left);
1036 let size = self.ts.math.size;
1037 let y = self.var_delimiter(left, size, xh + xd + clr + t, true);
1038 let (yh, yd) = self
1039 .nodes
1040 .box_data(y)
1041 .map_or((0, 0), |b| (b.height, b.depth));
1042 let theta = if let Some(th) = theta {
1043 let delta = yh + yd - th - (xh + xd + clr);
1046 if delta > 0 {
1047 clr += half(delta);
1048 }
1049 if let Some(b) = self.nodes.box_data_mut(y) {
1050 b.shift = yh - (xh + clr + th);
1051 }
1052 th
1053 } else {
1054 let top_h = if self.nodes.id(y) == id::VLIST {
1058 let h = self.nodes.list_ptr(y);
1059 self.nodes.box_data(h).map_or(yh, |b| b.height)
1060 } else {
1061 yh
1062 };
1063 let eq_depth = yh + yd - top_h;
1064 let delta = eq_depth - (xh + xd + clr);
1065 if delta > 0 {
1066 clr += half(delta);
1067 }
1068 if let Some(b) = self.nodes.box_data_mut(y) {
1069 b.shift = -(xh + clr) + (yh - top_h);
1070 }
1071 top_h
1072 };
1073 let top = if self.math_par_opt(mp::RADICAL_RULE).is_some() {
1074 kern
1075 } else {
1076 theta
1077 };
1078 let ob = self.overbar(x, clr, theta, top);
1079 self.nodes.link(y, ob);
1080 let deg = self.noad_field(q, Field::Degree);
1081 let list = if deg.is_null() {
1082 y
1083 } else {
1084 let r = self.clean_box(deg, 6);
1086 let (rw, rh, rd) = self
1087 .nodes
1088 .box_data(r)
1089 .map_or((0, 0, 0), |b| (b.width, b.height, b.depth));
1090 let _ = rd;
1091 let before = self.math_par(mp::RADICAL_DEGREE_BEFORE);
1092 let after = self.math_par(mp::RADICAL_DEGREE_AFTER);
1093 let raise = self.math_par(mp::RADICAL_DEGREE_RAISE);
1094 let (yh2, yd2, ys) = self
1095 .nodes
1096 .box_data(y)
1097 .map_or((0, 0, 0), |b| (b.height, b.depth, b.shift));
1098 let ytot = yh2 + yd2;
1099 let shift = retex_arith::xn_over_d(ytot, raise, 100).map_or(0, |r| r.quotient);
1100 if self.math_par_opt(mp::RADICAL_RULE).is_some() {
1103 if let Some(b) = self.nodes.box_data_mut(r) {
1104 b.shift = -(shift - (yd2 + ys));
1105 }
1106 } else if let Some(b) = self.nodes.box_data_mut(r) {
1107 b.shift = -(shift - yd2 + b.shift.min(0));
1108 }
1109 let _ = rh;
1110 let k1 = self.new_kern_node(before, kern_sub::FONT);
1111 let k2 = self.new_kern_node(after, kern_sub::FONT);
1112 let _ = rw;
1113 self.nodes.link(k1, r);
1114 self.nodes.link(r, k2);
1115 self.nodes.link(k2, y);
1116 k1
1117 };
1118 let b = self.hpack_quiet(list);
1119 self.set_nucleus_box(q, b);
1120 }
1121
1122 fn make_delimiter_radical(&mut self, q: NodeRef, sub: u16) {
1125 let k = self.noad_field(q, Field::Nucleus);
1126 let st = self.ts.math.style;
1127 let x = if k.is_null() {
1128 self.new_null_box_node(true)
1129 } else {
1130 self.clean_box(k, st)
1131 };
1132 let left = self.nodes.noad(q).map_or(NodeRef::NULL, |n| n.left);
1133 let width = self.nodes.noad(q).map_or(0, |n| n.width);
1134 let xw = self.nodes.box_data(x).map_or(0, |b| b.width);
1135 let size = self.ts.math.size;
1136 let (sf, sc, _, _) = self.delim_fields(left);
1137 let f = self.fam_fnt(sf, size);
1138 let y = if f != 0 && self.fonts.char_metrics(f, sc).is_some() {
1139 self.char_box(f, sc)
1140 } else {
1141 self.new_null_box_node(true)
1142 };
1143 if sub == radical_sub::UHEXTENSIBLE {
1144 let _ = width;
1145 self.nodes.flush_list(x);
1146 self.set_nucleus_box(q, y);
1147 return;
1148 }
1149 let y = self.rebox(y, xw.max(self.nodes.box_data(y).map_or(0, |b| b.width)));
1150 let x = self.rebox(x, self.nodes.box_data(y).map_or(0, |b| b.width));
1151 let (top, bottom) =
1152 if sub == radical_sub::UOVERDELIMITER || sub == radical_sub::UDELIMITEROVER {
1153 (y, x)
1154 } else {
1155 (x, y)
1156 };
1157 let gap = if sub == radical_sub::UOVERDELIMITER || sub == radical_sub::UDELIMITEROVER {
1158 self.math_par(mp::OVER_DELIMITER_VGAP)
1159 } else {
1160 self.math_par(mp::UNDER_DELIMITER_VGAP)
1161 };
1162 let kk = self.new_kern_node(gap, kern_sub::FONT);
1163 self.nodes.link(top, kk);
1164 self.nodes.link(kk, bottom);
1165 let v = self.vpack(top, PackSpec::NATURAL);
1166 let bh = self.nodes.box_data(bottom).map_or(0, |b| b.height);
1167 let bd = self.nodes.box_data(bottom).map_or(0, |b| b.depth);
1168 if let Some(b) = self.nodes.box_data_mut(v) {
1169 let tot = b.height + b.depth;
1170 if top == y {
1171 b.height = tot - bd;
1172 b.depth = bd;
1173 } else {
1174 b.height = bh;
1175 b.depth = tot - bh;
1176 }
1177 }
1178 self.set_nucleus_box(q, v);
1179 }
1180
1181 fn make_math_accent(&mut self, q: NodeRef) {
1183 let acc = self.nodes.noad(q).map_or(NodeRef::NULL, |n| n.accent);
1184 if acc.is_null() {
1185 self.make_bottom_accent(q);
1187 return;
1188 }
1189 let Some((f, mut c, mut i)) = self.fetch(acc) else {
1190 return;
1191 };
1192 let mut s = 0;
1194 let nuc = self.noad_field(q, Field::Nucleus);
1195 let mut nuc_char = None;
1196 if !nuc.is_null()
1197 && self.nodes.id(nuc) == id::MATH_CHAR
1198 && let Some((nf, nc, _)) = self.fetch(nuc)
1199 {
1200 nuc_char = Some((nf, nc));
1201 let sk = self.font(nf).skew_char;
1202 if sk >= 0
1203 && let Some(LigOp::Kern(k)) = self.lig_kern_step(nf, nc, sk as u32)
1204 {
1205 s = k;
1206 }
1207 }
1208 let st = cramped_style(self.ts.math.style);
1209 let mut char_nucleus = !nuc.is_null() && self.nodes.id(nuc) == id::MATH_CHAR;
1210 if nuc_char.is_none()
1211 && self.is_opentype_math_font(f)
1212 && let Some(fc) = self.nested_accent_char(nuc)
1213 {
1214 nuc_char = Some(fc);
1217 char_nucleus = true;
1218 }
1219 let mut x = self.clean_box(nuc, st);
1220 let (w, mut h) = self
1221 .nodes
1222 .box_data(x)
1223 .map_or((0, 0), |b| (b.width, b.height));
1224 let fixed = matches!(
1226 self.nodes.subtype(q),
1227 crate::nodes::accent_sub::FIXED_TOP | crate::nodes::accent_sub::FIXED_BOTH
1228 );
1229 if !fixed {
1230 loop {
1231 let (tag, rem) = self.char_tag(f, c);
1232 if tag != 2 {
1233 break;
1234 }
1235 let Some(m) = self.fonts.char_metrics(f, rem).copied() else {
1236 break;
1237 };
1238 if m.width > w {
1239 break;
1240 }
1241 c = rem;
1242 i = m;
1243 }
1244 }
1245 let _ = i;
1246 let opentype = self.is_opentype_math_font(f);
1247 let xh = if opentype {
1249 self.font(f)
1250 .math_constants
1251 .as_ref()
1252 .and_then(|m| m.get("AccentBaseHeight").copied())
1253 .unwrap_or_else(|| self.x_height(f))
1254 } else {
1255 self.x_height(f)
1256 };
1257 let mut delta = if h < xh { h } else { xh };
1258 let sup = self.noad_field(q, Field::Sup);
1259 let sub = self.noad_field(q, Field::Sub);
1260 if (!sup.is_null() || !sub.is_null())
1261 && !nuc.is_null()
1262 && self.nodes.id(nuc) == id::MATH_CHAR
1263 {
1264 self.nodes.flush_list(x);
1266 let n = self.new_noad(noad_sub::ORD);
1267 let nk = self.take_field(q, Field::Nucleus);
1268 let a = self.take_field(q, Field::Sup);
1269 let b = self.take_field(q, Field::Sub);
1270 self.set_noad_field(n, Field::Nucleus, nk);
1271 self.set_noad_field(n, Field::Sup, a);
1272 self.set_noad_field(n, Field::Sub, b);
1273 let k = self.new_sub_list(true, n);
1274 self.set_noad_field(q, Field::Nucleus, k);
1275 x = self.clean_box(k, self.ts.math.style);
1276 let nh = self.nodes.box_data(x).map_or(0, |b| b.height);
1277 delta = delta + nh - h;
1278 h = nh;
1279 }
1280 let y = self.char_box(f, c);
1281 let yw = self.nodes.box_data(y).map_or(0, |b| b.width);
1282 let shift = if opentype {
1283 let a = if char_nucleus {
1287 nuc_char
1288 .and_then(|(nf, nc)| self.math_char(nf, nc).and_then(|m| m.top_accent))
1289 .unwrap_or(half(w))
1290 } else {
1291 half(w)
1292 };
1293 let b = self
1294 .math_char(f, c)
1295 .and_then(|m| m.top_accent)
1296 .unwrap_or(half(yw));
1297 a - b
1298 } else if char_nucleus {
1299 s + half(w - yw)
1302 } else {
1303 s + w / 2 - yw / 2 + 1
1304 };
1305 if let Some(b) = self.nodes.box_data_mut(y) {
1306 b.shift = shift;
1307 b.width = 0;
1308 }
1309 let p = self.new_kern_node(-delta, kern_sub::FONT);
1310 self.nodes.link(p, x);
1311 self.nodes.link(y, p);
1312 let y = self.vpack(y, PackSpec::NATURAL);
1313 let mut xw = self.nodes.box_data(x).map_or(0, |b| b.width);
1314 if opentype
1317 && !self.nodes.next(q).is_null()
1318 && let Some((nf, nc)) = nuc_char
1319 && self.nodes.id(nuc) == id::MATH_CHAR
1320 {
1321 xw += self.fonts.char_metrics(nf, nc).map_or(0, |m| m.italic);
1322 }
1323 let yh = self.nodes.box_data(y).map_or(0, |b| b.height);
1324 if let Some(b) = self.nodes.box_data_mut(y) {
1325 b.width = xw;
1326 }
1327 if yh < h {
1328 let p = self.new_kern_node(h - yh, kern_sub::FONT);
1329 let l = self.nodes.list_ptr(y);
1330 self.nodes.link(p, l);
1331 self.nodes.set_list_ptr(y, p);
1332 if let Some(b) = self.nodes.box_data_mut(y) {
1333 b.height = h;
1334 }
1335 }
1336 self.set_nucleus_box(q, y);
1337 let bot = self.nodes.noad(q).map_or(NodeRef::NULL, |n| n.bot_accent);
1338 if !bot.is_null() {
1339 self.make_bottom_accent(q);
1340 }
1341 }
1342
1343 fn make_bottom_accent(&mut self, q: NodeRef) {
1346 let bot = match self.nodes.noad_mut(q) {
1347 Some(n) => std::mem::replace(&mut n.bot_accent, NodeRef::NULL),
1348 None => return,
1349 };
1350 let Some((f, mut c, _)) = self.fetch(bot) else {
1351 self.nodes.flush_list(bot);
1352 return;
1353 };
1354 self.nodes.flush_list(bot);
1355 let fixed = matches!(
1356 self.nodes.subtype(q),
1357 crate::nodes::accent_sub::FIXED_BOTTOM | crate::nodes::accent_sub::FIXED_BOTH
1358 );
1359 let nuc = self.noad_field(q, Field::Nucleus);
1360 let char_nucleus = !nuc.is_null() && self.nodes.id(nuc) == id::MATH_CHAR;
1361 let nuc_char = if char_nucleus {
1362 self.fetch(nuc).map(|(nf, nc, _)| (nf, nc))
1363 } else {
1364 None
1365 };
1366 let sup = self.noad_field(q, Field::Sup);
1368 let sub = self.noad_field(q, Field::Sub);
1369 let x = if char_nucleus && (!sup.is_null() || !sub.is_null()) {
1370 let n = self.new_noad(noad_sub::ORD);
1371 let nk = self.take_field(q, Field::Nucleus);
1372 let a = self.take_field(q, Field::Sup);
1373 let b = self.take_field(q, Field::Sub);
1374 self.set_noad_field(n, Field::Nucleus, nk);
1375 self.set_noad_field(n, Field::Sup, a);
1376 self.set_noad_field(n, Field::Sub, b);
1377 let k = self.new_sub_list(true, n);
1378 self.set_noad_field(q, Field::Nucleus, k);
1379 self.clean_box(k, self.ts.math.style)
1380 } else {
1381 self.clean_box(nuc, self.ts.math.style)
1382 };
1383 let (bw, xh) = self
1384 .nodes
1385 .box_data(x)
1386 .map_or((0, 0), |b| (b.width, b.height));
1387 let w = match nuc_char {
1390 Some((nf, nc)) => self.fonts.char_metrics(nf, nc).map_or(bw, |m| {
1391 if self.is_opentype_math_font(nf) {
1392 m.width
1393 } else {
1394 m.width + m.italic
1395 }
1396 }),
1397 None => bw,
1398 };
1399 if !fixed {
1401 loop {
1402 let (tag, rem) = self.char_tag(f, c);
1403 if tag != 2 {
1404 break;
1405 }
1406 match self.fonts.char_metrics(f, rem) {
1407 Some(m) if m.width <= w => c = rem,
1408 _ => break,
1409 }
1410 }
1411 }
1412 let y = self.char_box(f, c);
1413 let yw = self.nodes.box_data(y).map_or(0, |b| b.width);
1414 let shift = if self.is_opentype_math_font(f) {
1415 let a = nuc_char
1416 .filter(|_| self.noad_field(q, Field::Sup).is_null())
1417 .and_then(|(nf, nc)| self.math_char(nf, nc).and_then(|m| m.top_accent))
1418 .unwrap_or(half(w));
1419 let b = self
1420 .math_char(f, c)
1421 .and_then(|m| m.top_accent)
1422 .unwrap_or(half(yw));
1423 a - b
1424 } else if nuc_char.is_some() {
1425 half(w - yw)
1426 } else {
1427 w / 2 - yw / 2 + 1
1428 };
1429 if let Some(b) = self.nodes.box_data_mut(y) {
1430 b.shift = shift;
1431 b.width = 0;
1432 }
1433 self.nodes.link(x, y);
1434 let v = self.vpack(x, PackSpec::NATURAL);
1437 if let Some(b) = self.nodes.box_data_mut(v) {
1438 b.shift = b.height - xh;
1439 b.width = bw;
1440 }
1441 self.set_nucleus_box(q, v);
1442 }
1443
1444 fn make_fraction(&mut self, q: NodeRef) {
1446 let (mut thickness, num, denom, left, right, options) = match self.nodes.data(q) {
1447 NodeData::Fraction(f) => (f.thickness, f.num, f.denom, f.left, f.right, f.options),
1448 _ => return,
1449 };
1450 if thickness == DEFAULT_CODE {
1451 thickness = self.math_par(mp::FRACTION_RULE);
1452 }
1453 let cs = self.ts.math.style;
1454 let mut x = self.clean_box(num, num_style(cs));
1455 let mut z = self.clean_box(denom, denom_style(cs));
1456 let (xw, zw) = (
1457 self.nodes.box_data(x).map_or(0, |b| b.width),
1458 self.nodes.box_data(z).map_or(0, |b| b.width),
1459 );
1460 if xw < zw {
1461 x = self.rebox(x, zw);
1462 } else {
1463 z = self.rebox(z, xw);
1464 }
1465 let (mut shift_up, mut shift_down) = if thickness == 0 {
1466 (
1467 self.math_par(mp::STACK_NUM_UP),
1468 self.math_par(mp::STACK_DENOM_DOWN),
1469 )
1470 } else {
1471 (
1472 self.math_par(mp::FRACTION_NUM_UP),
1473 self.math_par(mp::FRACTION_DENOM_DOWN),
1474 )
1475 };
1476 let (xh, xd) = self
1477 .nodes
1478 .box_data(x)
1479 .map_or((0, 0), |b| (b.height, b.depth));
1480 let (zh, zd) = self
1481 .nodes
1482 .box_data(z)
1483 .map_or((0, 0), |b| (b.height, b.depth));
1484 let axis = self.math_par(mp::AXIS);
1485 let delta;
1486 if thickness == 0 {
1487 let clr = self.math_par(mp::STACK_VGAP);
1488 delta = half(clr - ((shift_up - xd) - (zh - shift_down)));
1489 if delta > 0 {
1490 shift_up += delta;
1491 shift_down += delta;
1492 }
1493 } else {
1494 let exact = options & noad_opt::EXACT == noad_opt::EXACT;
1495 let (clr_n, clr_d) = if exact {
1496 (0, 0)
1497 } else if thickness == self.math_par(mp::FRACTION_RULE) {
1498 (
1499 self.math_par(mp::FRACTION_NUM_VGAP),
1500 self.math_par(mp::FRACTION_DENOM_VGAP),
1501 )
1502 } else if cs < 2 {
1503 (3 * thickness, 3 * thickness)
1504 } else {
1505 (thickness, thickness)
1506 };
1507 delta = half(thickness);
1508 let delta1 = clr_n - ((shift_up - xd) - (axis + delta));
1509 let delta2 = clr_d - ((axis - delta) - (zh - shift_down));
1510 if delta1 > 0 {
1511 shift_up += delta1;
1512 }
1513 if delta2 > 0 {
1514 shift_down += delta2;
1515 }
1516 }
1517 let v = self.new_null_box_node(false);
1519 let width = self.nodes.box_data(x).map_or(0, |b| b.width);
1520 if let Some(b) = self.nodes.box_data_mut(v) {
1521 b.height = shift_up + xh;
1522 b.depth = zd + shift_down;
1523 b.width = width;
1524 }
1525 let p;
1526 if thickness == 0 {
1527 p = self.new_kern_node((shift_up - xd) - (zh - shift_down), kern_sub::FONT);
1528 self.nodes.link(p, z);
1529 } else {
1530 let y = self.fraction_rule(thickness);
1531 let p2 = self.new_kern_node((axis - delta) - (zh - shift_down), kern_sub::FONT);
1532 self.nodes.link(y, p2);
1533 self.nodes.link(p2, z);
1534 p = self.new_kern_node((shift_up - xd) - (axis + delta), kern_sub::FONT);
1535 self.nodes.link(p, y);
1536 }
1537 self.nodes.link(x, p);
1538 self.nodes.set_list_ptr(v, x);
1539 let dsize = self.math_par(mp::FRACTION_DEL_SIZE);
1541 let size = self.ts.math.size;
1542 let lx = self.var_delimiter(left, size, dsize, true);
1543 self.nodes.link(lx, v);
1544 let rz = self.var_delimiter(right, size, dsize, true);
1545 self.nodes.link(v, rz);
1546 let h = self.hpack_quiet(lx);
1547 self.set_new_hlist(q, h);
1548 }
1549
1550 fn make_op(&mut self, q: NodeRef) -> i32 {
1552 let cs = self.ts.math.style;
1553 if self.nodes.subtype(q) == noad_sub::OP_DISPLAY_LIMITS && cs < 2 {
1554 self.nodes.set_subtype(q, noad_sub::OP_LIMITS);
1555 }
1556 let limits = self.nodes.subtype(q) == noad_sub::OP_LIMITS;
1557 let nuc = self.noad_field(q, Field::Nucleus);
1558 let mut delta = 0;
1559 let mut opentype = false;
1560 if !nuc.is_null() && self.nodes.id(nuc) == id::MATH_CHAR {
1561 if let Some((f, c, m)) = self.fetch(nuc) {
1562 let mut m = m;
1563 if cs < 2 {
1564 let (tag, rem) = self.char_tag(f, c);
1565 if tag == 2
1566 && let Some(m2) = self.fonts.char_metrics(f, rem).copied()
1567 {
1568 m = m2;
1569 if let NodeData::MathChar { chr, .. } = self.nodes.data_mut(nuc) {
1570 *chr = rem;
1571 }
1572 }
1573 }
1574 delta = m.italic;
1575 opentype = self.is_opentype_math_font(f);
1576 }
1577 let x = self.clean_box(nuc, cs);
1578 let has_sub = !self.noad_field(q, Field::Sub).is_null();
1579 let axis = self.math_par(mp::AXIS);
1580 if let Some(b) = self.nodes.box_data_mut(x) {
1581 if has_sub && !limits && !opentype {
1582 b.width -= delta;
1583 }
1584 b.shift = half(b.height - b.depth) - axis;
1585 }
1586 self.set_nucleus_box(q, x);
1587 if opentype && !limits {
1591 if has_sub {
1592 self.ts.math.op_kern = -delta;
1593 } else {
1594 delta = 0;
1595 }
1596 }
1597 }
1598 if limits {
1599 let supk = self.noad_field(q, Field::Sup);
1601 let subk = self.noad_field(q, Field::Sub);
1602 let x = self.clean_box(supk, sup_style(cs));
1603 let nk = self.noad_field(q, Field::Nucleus);
1604 let y = self.clean_box(nk, cs);
1605 let z = self.clean_box(subk, sub_style(cs));
1606 let v = self.new_null_box_node(false);
1607 let wy = self.nodes.box_data(y).map_or(0, |b| b.width);
1608 let wx = self.nodes.box_data(x).map_or(0, |b| b.width);
1609 let wz = self.nodes.box_data(z).map_or(0, |b| b.width);
1610 let wv = wy.max(wx).max(wz);
1611 let x = self.rebox(x, wv);
1612 let y = self.rebox(y, wv);
1613 let z = self.rebox(z, wv);
1614 if let Some(b) = self.nodes.box_data_mut(x) {
1615 b.shift = half(delta);
1616 }
1617 if let Some(b) = self.nodes.box_data_mut(z) {
1618 b.shift = -half(delta);
1619 }
1620 let (yh, yd) = self
1621 .nodes
1622 .box_data(y)
1623 .map_or((0, 0), |b| (b.height, b.depth));
1624 let (mut vh, mut vd) = (yh, yd);
1625 let kern = self.math_par(mp::LIMIT_ABOVE_KERN);
1626 let kern_below = self.math_par(mp::LIMIT_BELOW_KERN);
1627 let head;
1628 if supk.is_null() {
1629 self.nodes.free(x);
1630 head = y;
1631 } else {
1632 let (xh, xd) = self
1633 .nodes
1634 .box_data(x)
1635 .map_or((0, 0), |b| (b.height, b.depth));
1636 let mut shift_up = self.math_par(mp::LIMIT_ABOVE_BGAP) - xd;
1637 let g1 = self.math_par(mp::LIMIT_ABOVE_VGAP);
1638 if shift_up < g1 {
1639 shift_up = g1;
1640 }
1641 let p = self.new_kern_node(shift_up, kern_sub::FONT);
1642 self.nodes.link(p, y);
1643 self.nodes.link(x, p);
1644 let p = self.new_kern_node(kern, kern_sub::FONT);
1645 self.nodes.link(p, x);
1646 head = p;
1647 vh += kern + xh + xd + shift_up;
1648 }
1649 if subk.is_null() {
1650 self.nodes.free(z);
1651 } else {
1652 let (zh, zd) = self
1653 .nodes
1654 .box_data(z)
1655 .map_or((0, 0), |b| (b.height, b.depth));
1656 let mut shift_down = self.math_par(mp::LIMIT_BELOW_BGAP) - zh;
1657 let g2 = self.math_par(mp::LIMIT_BELOW_VGAP);
1658 if shift_down < g2 {
1659 shift_down = g2;
1660 }
1661 let p = self.new_kern_node(shift_down, kern_sub::FONT);
1662 self.nodes.link(y, p);
1663 self.nodes.link(p, z);
1664 let p = self.new_kern_node(kern_below, kern_sub::FONT);
1665 self.nodes.link(z, p);
1666 vd += kern_below + zh + zd + shift_down;
1667 }
1668 if let Some(b) = self.nodes.box_data_mut(v) {
1669 b.width = wv;
1670 b.height = vh;
1671 b.depth = vd;
1672 b.head = head;
1673 }
1674 let a = self.take_field(q, Field::Sup);
1676 let b = self.take_field(q, Field::Sub);
1677 self.nodes.flush_list(a);
1678 self.nodes.flush_list(b);
1679 self.set_new_hlist(q, v);
1680 }
1681 delta
1682 }
1683
1684 fn make_ord(&mut self, q: NodeRef) {
1686 'restart: loop {
1687 if !self.noad_field(q, Field::Sub).is_null()
1688 || !self.noad_field(q, Field::Sup).is_null()
1689 {
1690 return;
1691 }
1692 let nuc = self.noad_field(q, Field::Nucleus);
1693 if nuc.is_null() || self.nodes.id(nuc) != id::MATH_CHAR {
1694 return;
1695 }
1696 let p = self.nodes.next(q);
1697 if p.is_null()
1698 || self.nodes.id(p) != id::NOAD
1699 || !matches!(
1700 self.nodes.subtype(p),
1701 noad_sub::ORD
1702 | noad_sub::OP_DISPLAY_LIMITS
1703 | noad_sub::OP_LIMITS
1704 | noad_sub::OP_NO_LIMITS
1705 | noad_sub::BIN
1706 | noad_sub::REL
1707 | noad_sub::OPEN
1708 | noad_sub::CLOSE
1709 | noad_sub::PUNCT
1710 )
1711 {
1712 return;
1713 }
1714 let pn = self.noad_field(p, Field::Nucleus);
1715 if pn.is_null() || self.nodes.id(pn) != id::MATH_CHAR {
1716 return;
1717 }
1718 let (qfam, pfam, pchr) = match (self.nodes.data(nuc), self.nodes.data(pn)) {
1719 (NodeData::MathChar { fam: a, .. }, NodeData::MathChar { fam: b, chr }) => {
1720 (*a, *b, *chr)
1721 }
1722 _ => return,
1723 };
1724 if qfam != pfam {
1725 return;
1726 }
1727 self.nodes.get_mut(nuc).id = id::MATH_TEXT_CHAR;
1728 let Some((f, c, _)) = self.fetch(nuc) else {
1729 return;
1730 };
1731 let Some(op) = self.lig_kern_step(f, c, pchr) else {
1732 return;
1733 };
1734 match op {
1735 LigOp::Kern(k) => {
1736 let kn = self.new_kern_node(k, kern_sub::FONT);
1737 let n = self.nodes.next(q);
1738 self.nodes.link(kn, n);
1739 self.nodes.link(q, kn);
1740 return;
1741 }
1742 LigOp::Lig { op, chr } => {
1743 match op {
1744 1 | 5 => self.set_char(nuc, chr),
1745 2 | 6 => self.set_char(pn, chr),
1746 3 | 7 | 11 => {
1747 let r = self.new_noad(noad_sub::ORD);
1748 let k = self.new_math_char(qfam, chr);
1749 if op >= 11 {
1750 self.nodes.get_mut(k).id = id::MATH_TEXT_CHAR;
1751 }
1752 self.set_noad_field(r, Field::Nucleus, k);
1753 self.nodes.link(q, r);
1754 self.nodes.link(r, p);
1755 }
1756 _ => {
1757 let n = self.nodes.next(p);
1758 self.nodes.link(q, n);
1759 if n.is_null() {
1760 self.nodes.set_next(q, NodeRef::NULL);
1761 }
1762 self.set_char(nuc, chr);
1763 let a = self.take_field(p, Field::Sub);
1764 let b = self.take_field(p, Field::Sup);
1765 self.set_noad_field(q, Field::Sub, a);
1766 self.set_noad_field(q, Field::Sup, b);
1767 self.nodes.set_next(p, NodeRef::NULL);
1768 self.nodes.flush_list(p);
1769 }
1770 }
1771 if op > 3 {
1772 return;
1773 }
1774 self.nodes.get_mut(nuc).id = id::MATH_CHAR;
1775 continue 'restart;
1776 }
1777 }
1778 }
1779 }
1780
1781 fn set_char(&mut self, k: NodeRef, c: u32) {
1782 if let NodeData::MathChar { chr, .. } = self.nodes.data_mut(k) {
1783 *chr = c;
1784 }
1785 }
1786
1787 fn convert_nucleus(&mut self, q: NodeRef, delta: i32) {
1790 let mut delta = delta;
1791 let nuc = self.noad_field(q, Field::Nucleus);
1792 let kid = if nuc.is_null() { 0 } else { self.nodes.id(nuc) };
1793 let has_sub = !self.noad_field(q, Field::Sub).is_null();
1794 let p = match kid {
1795 id::MATH_CHAR | id::MATH_TEXT_CHAR => match self.fetch(nuc) {
1796 Some((f, c, m)) => {
1797 delta = m.italic;
1798 let p = self.new_math_glyph(f, c);
1799 let has_sup = !self.noad_field(q, Field::Sup).is_null();
1800 if self.is_opentype_math_font(f) {
1801 if kid == id::MATH_CHAR && !has_sup && !has_sub {
1806 delta = 0;
1807 }
1808 } else if kid == id::MATH_TEXT_CHAR
1809 && self.font(f).params.get(2).copied().unwrap_or(0) != 0
1810 {
1811 delta = 0;
1812 }
1813 if !has_sub && delta != 0 {
1814 let k = self.new_kern_node(delta, kern_sub::ITALIC);
1815 self.nodes.link(p, k);
1816 delta = 0;
1817 }
1818 p
1819 }
1820 None => NodeRef::NULL,
1821 },
1822 id::SUB_BOX => {
1823 let b = self.nodes.list_ptr_kernel(nuc);
1824 self.nodes.set_kernel_list(nuc, NodeRef::NULL);
1825 let k = std::mem::take(&mut self.ts.math.op_kern);
1826 if k != 0 && !b.is_null() {
1827 let kn = self.new_kern_node(k, kern_sub::FONT);
1828 self.nodes.link(b, kn);
1829 }
1830 b
1831 }
1832 id::SUB_MLIST => {
1833 let l = self.nodes.list_ptr_kernel(nuc);
1834 self.nodes.set_kernel_list(nuc, NodeRef::NULL);
1835 let hl = self.clean_box_mlist(l, self.ts.math.style);
1836 self.hpack_quiet(hl)
1837 }
1838 _ => {
1839 if has_sub || !self.noad_field(q, Field::Sup).is_null() {
1841 self.new_null_box_node(true)
1842 } else {
1843 NodeRef::NULL
1844 }
1845 }
1846 };
1847 self.set_new_hlist(q, p);
1848 let has_sup = !self.noad_field(q, Field::Sup).is_null();
1849 if !has_sub && !has_sup {
1850 return;
1851 }
1852 self.make_scripts(q, delta);
1853 }
1854
1855 fn make_scripts(&mut self, q: NodeRef, delta: i32) {
1857 let p = self.get_new_hlist(q);
1858 let cs = self.ts.math.style;
1859 let (mut shift_up, mut shift_down);
1860 if !p.is_null() && self.nodes.is_glyph(p) {
1861 shift_up = 0;
1862 shift_down = 0;
1863 } else {
1864 let (h, d) = self.list_height_depth(p);
1865 shift_up = h - self.math_par(mp::SUP_SHIFT_DROP);
1866 shift_down = d + self.math_par(mp::SUB_SHIFT_DROP);
1867 }
1868 let opts = self.nodes.noad(q).map_or(0, |n| n.options);
1869 let supk = self.take_field(q, Field::Sup);
1870 let subk = self.take_field(q, Field::Sub);
1871 if !p.is_null() && self.nodes.is_glyph(p) {
1872 for k in [supk, subk] {
1875 if !k.is_null() && self.nodes.id(k) == id::MATH_CHAR {
1876 let _ = self.fetch(k);
1877 }
1878 }
1879 }
1880 let script_space = self.math_par(mp::SPACE_AFTER_SCRIPT);
1881 let x;
1882 if supk.is_null() {
1883 let st = if opts & noad_opt::NO_SUB_SCRIPT == noad_opt::NO_SUB_SCRIPT {
1885 cs
1886 } else {
1887 sub_style(cs)
1888 };
1889 x = self.clean_box(subk, st);
1890 self.nodes.flush_list(subk);
1891 let xh = self.nodes.box_data(x).map_or(0, |b| b.height);
1892 if let Some(b) = self.nodes.box_data_mut(x) {
1893 b.width += script_space;
1894 }
1895 let s1 = self.math_par(mp::SUB_SHIFT_DOWN);
1896 if shift_down < s1 {
1897 shift_down = s1;
1898 }
1899 let clr = xh - self.math_par(mp::SUB_TOP_MAX);
1900 if shift_down < clr {
1901 shift_down = clr;
1902 }
1903 if let Some(b) = self.nodes.box_data_mut(x) {
1904 b.shift = shift_down;
1905 }
1906 } else {
1907 let st = if opts & noad_opt::NO_SUPER_SCRIPT == noad_opt::NO_SUPER_SCRIPT {
1908 cs
1909 } else {
1910 sup_style(cs)
1911 };
1912 let sx = self.clean_box(supk, st);
1913 self.nodes.flush_list(supk);
1914 let xd = self.nodes.box_data(sx).map_or(0, |b| b.depth);
1915 if let Some(b) = self.nodes.box_data_mut(sx) {
1916 b.width += script_space;
1917 }
1918 let clr = self.math_par(mp::SUP_SHIFT_UP);
1919 if shift_up < clr {
1920 shift_up = clr;
1921 }
1922 let clr = xd + self.math_par(mp::SUP_BOTTOM_MIN);
1923 if shift_up < clr {
1924 shift_up = clr;
1925 }
1926 if subk.is_null() {
1927 if let Some(b) = self.nodes.box_data_mut(sx) {
1928 b.shift = -shift_up;
1929 }
1930 x = sx;
1931 } else {
1932 let st = if opts & noad_opt::NO_SUB_SCRIPT == noad_opt::NO_SUB_SCRIPT {
1933 cs
1934 } else {
1935 sub_style(cs)
1936 };
1937 let y = self.clean_box(subk, st);
1938 self.nodes.flush_list(subk);
1939 let yh = self.nodes.box_data(y).map_or(0, |b| b.height);
1940 if let Some(b) = self.nodes.box_data_mut(y) {
1941 b.width += script_space;
1942 }
1943 let s2 = self.math_par(mp::SUB_SUP_SHIFT_DOWN);
1944 if shift_down < s2 {
1945 shift_down = s2;
1946 }
1947 let mut clr =
1948 self.math_par(mp::SUB_SUP_VGAP) - ((shift_up - xd) - (yh - shift_down));
1949 if clr > 0 {
1950 shift_down += clr;
1951 clr = self.math_par(mp::SUP_SUB_BOTTOM_MAX) - (shift_up - xd);
1952 if clr > 0 {
1953 shift_up += clr;
1954 shift_down -= clr;
1955 }
1956 }
1957 if let Some(b) = self.nodes.box_data_mut(sx) {
1958 b.shift = delta;
1959 }
1960 let p = self.new_kern_node((shift_up - xd) - (yh - shift_down), kern_sub::FONT);
1961 self.nodes.link(sx, p);
1962 self.nodes.link(p, y);
1963 x = self.vpack(sx, PackSpec::NATURAL);
1964 if let Some(b) = self.nodes.box_data_mut(x) {
1965 b.shift = shift_down;
1966 }
1967 }
1968 }
1969 let nh = self.get_new_hlist(q);
1970 if nh.is_null() {
1971 self.set_new_hlist(q, x);
1972 } else {
1973 let t = self.nodes.tail_of(nh);
1974 self.nodes.link(t, x);
1975 }
1976 }
1977
1978 fn make_left_right(&mut self, q: NodeRef, style: i32, max_d: i32, max_h: i32) -> NoadType {
1980 self.set_cur_style(style);
1981 let (delim, height, depth, options) = match self.nodes.data(q) {
1982 NodeData::Fence(f) => (f.delim, f.height, f.depth, f.options),
1983 _ => (NodeRef::NULL, 0, 0, 0),
1984 };
1985 let axis = self.math_par(mp::AXIS);
1986 let b = if height != 0 || depth != 0 {
1987 let total = height + depth;
1988 let b = self.var_delimiter(delim, self.ts.math.size, total, true);
1989 if options & noad_opt::NO_AXIS == noad_opt::NO_AXIS
1990 && let Some(bd) = self.nodes.box_data_mut(b)
1991 {
1992 bd.shift = 0;
1993 }
1994 b
1995 } else {
1996 let mut delta2 = max_d + axis;
1997 let mut delta1 = max_h + max_d - delta2;
1998 if delta2 > delta1 {
1999 delta1 = delta2;
2000 }
2001 let mut delta = (delta1 / 500) * self.eqtb.int(ip::DELIMITERFACTOR);
2002 delta2 = delta1 + delta1 - self.eqtb.dimen(dp::DELIMITERSHORTFALL);
2003 if delta < delta2 {
2004 delta = delta2;
2005 }
2006 self.var_delimiter(delim, self.ts.math.size, delta, false)
2007 };
2008 self.set_new_hlist(q, b);
2009 if self.nodes.subtype(q) == fence_sub::LEFT {
2010 NoadType::Open
2011 } else {
2012 NoadType::Close
2013 }
2014 }
2015
2016 fn second_pass(
2019 &mut self,
2020 mlist: NodeRef,
2021 style: i32,
2022 penalties: bool,
2023 max_h: i32,
2024 max_d: i32,
2025 ) -> NodeRef {
2026 let head = self.nodes.new_temp();
2027 let mut p = head;
2028 let mut q = mlist;
2029 let mut r_type: Option<NoadType> = None;
2030 self.set_cur_style(style);
2031 let binpen = self.eqtb.int(ip::BINOPPENALTY);
2032 let relpen = self.eqtb.int(ip::RELPENALTY);
2033 while !q.is_null() {
2034 let next = self.nodes.next(q);
2035 let Some(nt) = self.noad_type(q) else {
2036 if let NodeData::Style { style } = self.nodes.data(q) {
2037 let s = i32::from(*style);
2038 self.set_cur_style(s);
2039 self.nodes.free(q);
2040 } else {
2041 self.nodes.link(p, q);
2042 self.nodes.set_next(q, NodeRef::NULL);
2043 p = q;
2044 }
2045 q = next;
2046 continue;
2047 };
2048 let mut t = NoadType::Ord;
2049 let mut pen = 10000;
2050 match nt {
2051 NoadType::Op
2052 | NoadType::Open
2053 | NoadType::Close
2054 | NoadType::Punct
2055 | NoadType::Inner => t = nt,
2056 NoadType::Bin => {
2057 t = NoadType::Bin;
2058 pen = binpen;
2059 }
2060 NoadType::Rel => {
2061 t = NoadType::Rel;
2062 pen = relpen;
2063 }
2064 NoadType::Fraction => t = NoadType::Inner,
2065 NoadType::Left | NoadType::Right => {
2066 t = self.make_left_right(q, style, max_d, max_h);
2067 }
2068 _ => {}
2069 }
2070 if let Some(rt) = r_type {
2072 let style = self.ts.math.style as u32;
2073 if let Some((g, sub)) = self.math_spacing(rt as u32, t as u32, style) {
2074 let y = self.math_glue(g, self.ts.math.mu);
2075 let z = self.new_glue_node(y, sub);
2076 self.nodes.link(p, z);
2077 p = z;
2078 }
2079 }
2080 let nh = self.get_new_hlist(q);
2082 self.set_new_hlist(q, NodeRef::NULL);
2083 if !nh.is_null() {
2084 self.nodes.link(p, nh);
2085 p = self.nodes.tail_of(nh);
2086 }
2087 if penalties && !next.is_null() && pen < 10000 {
2088 let nid = self.nodes.id(next);
2089 let next_is_rel = self.noad_type(next) == Some(NoadType::Rel);
2090 if nid != id::PENALTY && !next_is_rel {
2091 let z = self.new_penalty_node(pen);
2092 self.nodes.set_subtype(z, 5);
2093 self.nodes.link(p, z);
2094 p = z;
2095 }
2096 }
2097 if nt == NoadType::Right {
2098 t = NoadType::Open;
2099 }
2100 r_type = Some(t);
2101 self.nodes.set_next(q, NodeRef::NULL);
2103 self.nodes.flush_list(q);
2104 q = next;
2105 }
2106 let first = self.nodes.next(head);
2107 self.nodes.free(head);
2108 if !first.is_null() {
2109 self.nodes.set_prev(first, NodeRef::NULL);
2110 }
2111 first
2112 }
2113}
2114
2115#[derive(Clone, Copy, PartialEq, Eq)]
2116enum Action {
2117 Convert,
2118 CheckDimensions,
2119 DoneWithNoad,
2120}