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 AttrList, DEFAULT_CODE, NodeData, NodeRef, fence_sub, glue_sub, id, kern_sub, list_sub,
26 noad_opt, noad_sub, 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 saved_attr = self.ts.attr_override.clone();
192 let r = self.mlist_to_hlist_rec(mlist, style, penalties);
193 self.ts.math = saved;
194 self.ts.attr_override = saved_attr;
195 r
196 }
197
198 fn attr_of(&self, p: NodeRef) -> Option<AttrList> {
200 if p.is_null() {
201 None
202 } else {
203 self.nodes.attr(p).cloned()
204 }
205 }
206
207 fn attr_set(&mut self, a: Option<AttrList>) -> Option<Option<AttrList>> {
209 self.ts.attr_override.replace(a)
210 }
211
212 fn attr_from(&mut self, p: NodeRef) -> Option<Option<AttrList>> {
217 let a = self.attr_of(p);
218 self.attr_set(a)
219 }
220
221 fn attr_restore(&mut self, saved: Option<Option<AttrList>>) {
222 self.ts.attr_override = saved;
223 }
224
225 fn get_new_hlist(&self, q: NodeRef) -> NodeRef {
226 match self.nodes.data(q) {
227 NodeData::Noad(n) => n.new_hlist,
228 NodeData::Fraction(f) => f.new_hlist,
229 NodeData::Fence(f) => f.new_hlist,
230 _ => NodeRef::NULL,
231 }
232 }
233
234 fn set_new_hlist(&mut self, q: NodeRef, p: NodeRef) {
235 match self.nodes.data_mut(q) {
236 NodeData::Noad(n) => n.new_hlist = p,
237 NodeData::Fraction(f) => f.new_hlist = p,
238 NodeData::Fence(f) => f.new_hlist = p,
239 _ => {}
240 }
241 }
242
243 fn mlist_to_hlist_rec(&mut self, mlist: NodeRef, style: i32, penalties: bool) -> NodeRef {
244 let mut q = mlist;
245 let mut r = NodeRef::NULL;
246 let mut r_type = NoadType::Op;
247 let mut max_h = 0i32;
248 let mut max_d = 0i32;
249 self.set_cur_style(style);
250 while !q.is_null() {
251 let mut delta = 0;
253 let Some(mut t) = self.noad_type(q) else {
254 let next = self.first_pass_node(q, &mut max_h, &mut max_d);
256 q = next;
257 continue;
258 };
259 let saved_attr = self.attr_from(q);
260 let mut action = Action::Convert;
261 loop {
262 match t {
263 NoadType::Bin => {
264 if matches!(
265 r_type,
266 NoadType::Bin
267 | NoadType::Op
268 | NoadType::Rel
269 | NoadType::Open
270 | NoadType::Punct
271 | NoadType::Left
272 ) {
273 self.nodes.set_subtype(q, noad_sub::ORD);
274 t = NoadType::Ord;
275 continue;
276 }
277 }
278 NoadType::Rel | NoadType::Close | NoadType::Punct | NoadType::Right => {
279 if r_type == NoadType::Bin {
280 self.nodes.set_subtype(r, noad_sub::ORD);
281 }
282 if t == NoadType::Right {
283 action = Action::DoneWithNoad;
284 }
285 }
286 NoadType::Left => action = Action::DoneWithNoad,
287 NoadType::Fraction => {
288 self.make_fraction(q);
289 action = Action::CheckDimensions;
290 }
291 NoadType::Op => {
292 delta = self.make_op(q);
293 if self.nodes.subtype(q) == noad_sub::OP_LIMITS {
294 action = Action::CheckDimensions;
295 }
296 }
297 NoadType::Ord => self.make_ord(q),
298 NoadType::Open | NoadType::Inner => {}
299 NoadType::Radical => self.make_radical(q),
300 NoadType::Over => self.make_over(q),
301 NoadType::Under => self.make_under(q),
302 NoadType::Accent => self.make_math_accent(q),
303 NoadType::Vcenter => self.make_vcenter(q),
304 }
305 break;
306 }
307 if action == Action::Convert {
308 self.convert_nucleus(q, delta);
309 }
310 if action != Action::DoneWithNoad {
311 let nh = self.get_new_hlist(q);
313 let (h, d) = self.list_height_depth(nh);
314 if h > max_h {
315 max_h = h;
316 }
317 if d > max_d {
318 max_d = d;
319 }
320 }
321 r = q;
323 r_type = self.noad_type(r).unwrap_or(NoadType::Ord);
324 if r_type == NoadType::Right {
325 r_type = NoadType::Left;
326 self.set_cur_style(style);
327 }
328 self.attr_restore(saved_attr);
329 q = self.nodes.next(q);
330 }
331 if r_type == NoadType::Bin {
332 self.nodes.set_subtype(r, noad_sub::ORD);
333 }
334 self.second_pass(mlist, style, penalties, max_h, max_d)
335 }
336
337 fn list_height_depth(&mut self, p: NodeRef) -> (i32, i32) {
339 let saved = self.ts.adjust_tail.take();
340 let pre = self.ts.pre_adjust_tail.take();
341 let z = self.hpack_quiet(p);
342 self.ts.adjust_tail = saved;
343 self.ts.pre_adjust_tail = pre;
344 let (h, d) = self
345 .nodes
346 .box_data(z)
347 .map_or((0, 0), |b| (b.height, b.depth));
348 self.nodes.set_list_ptr(z, NodeRef::NULL);
349 self.nodes.free(z);
350 (h, d)
351 }
352
353 fn hpack_quiet(&mut self, p: NodeRef) -> NodeRef {
356 self.hpack(p, PackSpec::NATURAL)
357 }
358
359 fn first_pass_node(&mut self, q: NodeRef, max_h: &mut i32, max_d: &mut i32) -> NodeRef {
361 match self.nodes.data(q) {
362 NodeData::Style { style } => {
363 let s = i32::from(*style);
364 self.set_cur_style(s);
365 }
366 NodeData::Choice(c) => {
367 let c = **c;
368 let k = (self.ts.math.style / 2) as usize;
369 let p = c[k.min(3)];
370 for (i, l) in c.iter().enumerate() {
371 if i != k.min(3) {
372 self.nodes.flush_list(*l);
373 }
374 }
375 let st = self.ts.math.style as u8;
376 *self.nodes.get_mut(q) = crate::nodes::Node {
377 data: NodeData::Style { style: st },
378 id: id::STYLE,
379 subtype: u16::from(st),
380 ..self.nodes.get(q).clone()
381 };
382 if !p.is_null() {
383 let z = self.nodes.next(q);
384 self.nodes.link(q, p);
385 let t = self.nodes.tail_of(p);
386 self.nodes.link(t, z);
387 }
388 }
389 NodeData::Rule { height, depth, .. } => {
390 if *height > *max_h {
391 *max_h = *height;
392 }
393 if *depth > *max_d {
394 *max_d = *depth;
395 }
396 }
397 NodeData::Glue { spec, .. } => {
398 let spec = *spec;
399 let sub = self.nodes.subtype(q);
400 if sub == glue_sub::MU_GLUE {
401 let g = self.math_glue(spec, self.ts.math.mu);
402 if let NodeData::Glue { spec, .. } = self.nodes.data_mut(q) {
403 *spec = g;
404 }
405 self.nodes.set_subtype(q, glue_sub::USER);
406 } else if self.ts.math.size != 0 && sub == glue_sub::COND_MATH_GLUE {
407 let p = self.nodes.next(q);
408 if !p.is_null() && matches!(self.nodes.id(p), id::GLUE | id::KERN) {
409 let n = self.nodes.next(p);
410 self.nodes.link(q, n);
411 if n.is_null() {
412 self.nodes.set_next(q, NodeRef::NULL);
413 }
414 self.nodes.set_next(p, NodeRef::NULL);
415 self.nodes.flush_list(p);
416 }
417 }
418 }
419 NodeData::Kern { kern, .. } => {
420 if self.nodes.subtype(q) == glue_sub::MU_GLUE {
421 let w = self.mu_mult(*kern, self.ts.math.mu);
422 if let NodeData::Kern { kern, .. } = self.nodes.data_mut(q) {
423 *kern = w;
424 }
425 self.nodes.set_subtype(q, kern_sub::ITALIC);
427 }
428 }
429 _ => {}
430 }
431 self.nodes.next(q)
432 }
433
434 fn mu_mult(&self, x: i32, m: i32) -> i32 {
436 let r = retex_arith::x_over_n_unchecked(m, 0o200000);
437 let (mut n, mut f) = (r.quotient, r.remainder);
438 if f < 0 {
439 n -= 1;
440 f += 0o200000;
441 }
442 let frac = retex_arith::xn_over_d(x, f, 0o200000).map_or(0, |r| r.quotient);
443 retex_arith::nx_plus_y(n, x, frac).unwrap_or(0)
444 }
445
446 pub fn math_glue(&self, g: crate::glue::Glue, m: i32) -> crate::glue::Glue {
448 let mut r = g;
449 r.width = self.mu_mult(g.width, m);
450 if g.stretch_order == 0 {
451 r.stretch = self.mu_mult(g.stretch, m);
452 }
453 if g.shrink_order == 0 {
454 r.shrink = self.mu_mult(g.shrink, m);
455 }
456 r
457 }
458
459 fn fetch(&mut self, k: NodeRef) -> Option<(u32, u32, crate::fonts::CharMetrics)> {
464 let (fam, chr) = match self.nodes.data(k) {
465 NodeData::MathChar { fam, chr } => (*fam, *chr),
466 _ => return None,
467 };
468 let size = self.ts.math.size;
469 let f = self.fam_fnt(fam, size);
470 if f == 0 {
471 self.print_err("");
472 self.print_size(size);
473 self.printer.print_int(i64::from(fam));
474 self.printer.print_str(" is undefined (character ");
475 self.printer.print_int(i64::from(chr));
476 self.printer.print_char(')' as u32);
477 self.help(&[
478 "Somewhere in the math formula just ended, you used the",
479 "stated character from an undefined font family. For example,",
480 "plain TeX doesn't allow \\it or \\sl in subscripts. Proceed,",
481 "and I'll try to forget that I needed that character.",
482 ]);
483 self.error();
484 return None;
485 }
486 match self.fonts.char_metrics(f, chr) {
487 Some(m) => Some((f, chr, *m)),
488 None => {
489 self.char_warning(f, chr);
490 None
491 }
492 }
493 }
494
495 pub fn print_size(&mut self, s: u32) {
498 self.print_esc_str(match s {
499 0 => "textfont",
500 1 => "scriptfont",
501 _ => "scriptscriptfont",
502 });
503 }
504
505 pub fn char_warning(&mut self, f: u32, c: u32) {
508 if self.eqtb.int(ip::TRACINGLOSTCHARS) > 0 {
509 let old = self.eqtb.int(ip::TRACINGONLINE);
510 self.begin_diagnostic();
511 self.printer.print_nl("Missing character: There is no ");
512 self.printer.print_char(c);
513 self.printer.print_str(&format!(" (U+{c:04X}) in font "));
515 let name = self.font(f).name.clone();
516 self.printer.print_bytes(&name);
517 self.printer.print_char('!' as u32);
518 self.end_diagnostic(false);
519 let _ = old;
520 }
521 }
522
523 fn new_math_glyph(&mut self, f: u32, c: u32) -> NodeRef {
525 let g = self.new_char_node(f, c);
526 self.nodes.set_subtype(g, PROTECTED_GLYPH);
527 g
528 }
529
530 fn char_box(&mut self, f: u32, c: u32) -> NodeRef {
533 let m = self.fonts.char_metrics(f, c).copied().unwrap_or_default();
534 let italic = if self.is_opentype_math_font(f) {
536 0
537 } else {
538 m.italic
539 };
540 let b = self.new_null_box_node(true);
541 let g = self.new_math_glyph(f, c);
542 if let Some(bd) = self.nodes.box_data_mut(b) {
543 bd.width = m.width + italic;
544 bd.height = m.height;
545 bd.depth = m.depth;
546 bd.head = g;
547 }
548 self.nodes.set_subtype(b, list_sub::MATH_CHAR);
549 b
550 }
551
552 fn char_tag(&self, f: u32, c: u32) -> (u8, u32) {
555 if let Some(t) = self.tfm_of(f)
556 && let Some(ci) = t.char_info(c)
557 {
558 return (ci.tag, u32::from(ci.remainder));
559 }
560 if let Some(m) = self.math_char(f, c) {
561 if let Some(n) = m.next {
562 return (2, n);
563 }
564 if m.vert.is_some() {
565 return (3, c);
566 }
567 }
568 (0, 0)
569 }
570
571 fn clean_box(&mut self, k: NodeRef, s: i32, sub: u16) -> NodeRef {
576 let mut q;
577 let kid = if k.is_null() { 0 } else { self.nodes.id(k) };
578 match kid {
579 id::MATH_CHAR | id::MATH_TEXT_CHAR => {
580 let n = self.new_noad(noad_sub::ORD);
581 let kc = self.nodes.copy_node(k);
582 self.set_noad_field(n, Field::Nucleus, kc);
583 q = self.clean_box_mlist(n, s);
584 }
585 id::SUB_BOX => {
586 q = self.nodes.list_ptr_kernel(k);
587 self.nodes.set_kernel_list(k, NodeRef::NULL);
588 }
589 id::SUB_MLIST => {
590 let l = self.nodes.list_ptr_kernel(k);
591 self.nodes.set_kernel_list(k, NodeRef::NULL);
592 q = self.clean_box_mlist(l, s);
593 }
594 _ => {
595 q = self.new_null_box_node(true);
596 }
597 }
598 let x = if q.is_null() {
601 self.hpack_quiet(q)
602 } else if !self.nodes.is_glyph(q)
603 && self.nodes.next(q).is_null()
604 && matches!(self.nodes.id(q), id::HLIST | id::VLIST)
605 && self.nodes.box_data(q).is_some_and(|b| b.shift == 0)
606 {
607 q
608 } else {
609 let saved = self.attr_from(q);
610 let x = self.hpack_quiet(q);
611 self.attr_restore(saved);
612 x
613 };
614 self.nodes.set_subtype(x, sub);
615 q = self.nodes.list_ptr(x);
617 if !q.is_null() && self.nodes.is_glyph(q) {
618 let r = self.nodes.next(q);
619 if !r.is_null() && self.nodes.next(r).is_null() && self.nodes.id(r) == id::KERN {
620 self.nodes.free(r);
621 self.nodes.set_next(q, NodeRef::NULL);
622 }
623 }
624 x
625 }
626
627 fn clean_box_mlist(&mut self, l: NodeRef, s: i32) -> NodeRef {
628 let saved = self.ts.math;
629 let r = self.mlist_to_hlist_rec(l, s, false);
630 self.ts.math = saved;
631 r
632 }
633
634 fn rebox(&mut self, b: NodeRef, w: i32) -> NodeRef {
636 let bw = self.nodes.box_data(b).map_or(0, |x| x.width);
637 let lp = self.nodes.list_ptr(b);
638 if bw != w && !lp.is_null() {
639 let saved = self.attr_from(b);
641 let mut b = b;
642 if self.nodes.id(b) == id::VLIST {
643 b = self.hpack_quiet(b);
644 }
645 let mut p = self.nodes.list_ptr(b);
646 let bw = self.nodes.box_data(b).map_or(0, |x| x.width);
647 if self.nodes.is_glyph(p) && self.nodes.next(p).is_null() {
648 let (f, c) = {
649 let g = self.nodes.glyph(p).expect("glyph");
650 (g.font, g.chr)
651 };
652 let v = self.fonts.char_metrics(f, c).map_or(0, |m| m.width);
653 if v != bw {
654 let k = self.new_kern_node(bw - v, kern_sub::FONT);
655 self.nodes.link(p, k);
656 }
657 }
658 self.nodes.set_list_ptr(b, NodeRef::NULL);
659 self.nodes.free(b);
660 let ss = crate::glue::Glue {
661 stretch: retex_arith::UNITY,
662 stretch_order: crate::glue::FIL,
663 shrink: retex_arith::UNITY,
664 shrink_order: crate::glue::FIL,
665 ..crate::glue::Glue::ZERO
666 };
667 let g1 = self.new_glue_node(ss, glue_sub::USER);
668 self.nodes.link(g1, p);
669 while !self.nodes.next(p).is_null() {
670 p = self.nodes.next(p);
671 }
672 let g2 = self.new_glue_node(ss, glue_sub::USER);
673 self.nodes.link(p, g2);
674 let r = self.hpack(g1, PackSpec::Exactly(w));
675 self.attr_restore(saved);
676 r
677 } else {
678 if let Some(x) = self.nodes.box_data_mut(b) {
679 x.width = w;
680 }
681 b
682 }
683 }
684
685 fn fraction_rule(&mut self, t: i32) -> NodeRef {
687 self.new_node(
688 id::RULE,
689 0,
690 NodeData::Rule {
691 width: crate::typeset::NULL_FLAG,
692 depth: 0,
693 height: t,
694 dir: 0,
695 index: 0,
696 left: 0,
697 right: 0,
698 },
699 )
700 }
701
702 fn overbar(&mut self, b: NodeRef, k: i32, t: i32, top: i32) -> NodeRef {
704 let p = self.new_kern_node(k, kern_sub::FONT);
705 self.nodes.link(p, b);
706 let q = self.fraction_rule(t);
707 self.nodes.link(q, p);
708 let p = self.new_kern_node(top, kern_sub::FONT);
709 self.nodes.link(p, q);
710 self.vpack(p, PackSpec::NATURAL)
711 }
712
713 pub fn var_delimiter(&mut self, d: NodeRef, s: u32, v: i32, shift: bool) -> NodeRef {
721 let saved = self.attr_from(d);
722 let b = self.var_delimiter_box(d, s, v, shift);
723 self.attr_restore(saved);
724 b
725 }
726
727 fn var_delimiter_box(&mut self, d: NodeRef, s: u32, v: i32, shift: bool) -> NodeRef {
728 let (sf, sc, lf, lc) = self.delim_fields(d);
729 let mut best: Option<(u32, u32, bool)> = None;
730 let mut w = 0i32;
731 'search: for (z0, x0) in [(sf, sc), (lf, lc)] {
732 if z0 == 0 && x0 == 0 {
733 continue;
734 }
735 let mut z = s as i32;
736 while z >= 0 {
737 let g = self.fam_fnt(z0, z as u32);
738 if g != 0 {
739 let mut y = x0;
740 let mut guard = 0;
741 loop {
742 guard += 1;
743 if guard > 256 {
744 break;
745 }
746 let Some(m) = self.fonts.char_metrics(g, y).copied() else {
747 break;
748 };
749 let (tag, rem) = self.char_tag(g, y);
750 let opentype = self.is_opentype_math_font(g);
753 if tag == 3 && !opentype {
754 best = Some((g, y, true));
755 break 'search;
756 }
757 let u = m.height + m.depth;
758 if u > w {
759 best = Some((g, y, false));
760 w = u;
761 if u >= v {
762 break 'search;
763 }
764 }
765 if opentype && self.vert_parts(g, y).is_some() {
766 best = Some((g, y, true));
767 break 'search;
768 }
769 if tag == 2 {
770 y = rem;
771 } else {
772 break;
773 }
774 }
775 }
776 z -= 1;
777 }
778 }
779 let b = match best {
780 Some((f, c, true)) if self.is_opentype_math_font(f) => {
781 let saved = self.ts.math;
782 self.set_cur_style(if s == 0 { 2 } else { 2 * s as i32 + 2 });
783 let overlap = self.math_par(mp::CONNECTOR_OVERLAP_MIN);
784 self.ts.math = saved;
785 self.ot_extensible_box(f, c, v, overlap)
786 }
787 Some((f, c, true)) => self.extensible_box(f, c, v),
788 Some((f, c, false)) => self.char_box(f, c),
789 None => {
790 let b = self.new_null_box_node(true);
791 let nds = self.eqtb.dimen(dp::NULLDELIMITERSPACE);
792 if let Some(bd) = self.nodes.box_data_mut(b) {
793 bd.width = nds;
794 }
795 self.nodes.set_subtype(b, list_sub::V_DELIMITER);
796 if !shift {
797 return b;
798 }
799 b
800 }
801 };
802 let saved = self.ts.math;
803 self.set_cur_style(if s == 0 { 2 } else { 2 * s as i32 + 2 });
804 let axis = self.math_par(mp::AXIS);
805 self.ts.math = saved;
806 if let Some(bd) = self.nodes.box_data_mut(b) {
807 bd.shift = half(bd.height - bd.depth) - axis;
808 }
809 b
810 }
811
812 fn nested_accent_char(&mut self, k: NodeRef) -> Option<(u32, u32)> {
815 if k.is_null() || self.nodes.id(k) != id::SUB_MLIST {
816 return None;
817 }
818 let l = self.nodes.list_ptr_kernel(k);
819 if l.is_null()
820 || !self.nodes.next(l).is_null()
821 || self.noad_type(l) != Some(NoadType::Accent)
822 {
823 return None;
824 }
825 let n = self.noad_field(l, Field::Nucleus);
826 if n.is_null() {
827 return None;
828 }
829 if self.nodes.id(n) == id::MATH_CHAR {
830 return self.fetch(n).map(|(f, c, _)| (f, c));
831 }
832 self.nested_accent_char(n)
833 }
834
835 fn vert_parts(&self, f: u32, c: u32) -> Option<std::rc::Rc<[crate::fonts::ExtPart]>> {
837 self.math_char(f, c)?.vert.clone()
838 }
839
840 fn ot_extensible_box(&mut self, f: u32, c: u32, v: i32, overlap: i32) -> NodeRef {
848 let parts = self
849 .vert_parts(f, c)
850 .unwrap_or_else(|| std::rc::Rc::from(Vec::new()));
851 let expand = |n: usize| -> Vec<crate::fonts::ExtPart> {
852 let mut out = Vec::new();
853 for p in parts.iter() {
854 let k = if p.extender { n } else { 1 };
855 for _ in 0..k {
856 out.push(*p);
857 }
858 }
859 out
860 };
861 let joint = |a: &crate::fonts::ExtPart, b: &crate::fonts::ExtPart| {
862 let max = a.end.min(b.start).max(0);
863 (max, overlap.min(max))
864 };
865 let largest = |seq: &[crate::fonts::ExtPart]| -> i64 {
866 let mut t: i64 = seq.iter().map(|p| i64::from(p.advance)).sum();
867 for w in seq.windows(2) {
868 t -= i64::from(joint(&w[0], &w[1]).1);
869 }
870 t
871 };
872 let has_extender = parts.iter().any(|p| p.extender);
873 let mut n = 0;
874 let mut seq = expand(n);
875 while has_extender && largest(&seq) < i64::from(v) && n < 1000 {
876 n += 1;
877 seq = expand(n);
878 }
879 let mut head = NodeRef::NULL;
881 let mut tail = NodeRef::NULL;
882 let mut width = 0;
883 for (i, p) in seq.iter().enumerate().rev() {
884 if i + 1 < seq.len() {
885 let (max, min) = joint(p, &seq[i + 1]);
886 let g = crate::glue::Glue {
887 width: -max,
888 stretch: max - min,
889 ..crate::glue::Glue::ZERO
890 };
891 let gn = self.new_glue_node(g, 0);
892 self.nodes.link(tail, gn);
893 tail = gn;
894 }
895 let m = self
896 .fonts
897 .char_metrics(f, p.code)
898 .copied()
899 .unwrap_or_default();
900 let b = self.new_null_box_node(true);
901 self.nodes.set_subtype(b, list_sub::MATH_CHAR);
902 let gl = self.new_math_glyph(f, p.code);
903 if let Some(bd) = self.nodes.box_data_mut(b) {
907 bd.width = m.width;
908 bd.height = m.height + m.depth;
909 bd.depth = 0;
910 bd.head = gl;
911 }
912 width = width.max(m.width);
913 if head.is_null() {
914 head = b;
915 } else {
916 self.nodes.link(tail, b);
917 }
918 tail = b;
919 }
920 let natural = largest(&seq) as i32;
921 let target = v.max(natural.min(v));
922 let b = self.vpack(head, crate::build::PackSpec::Exactly(target));
923 self.nodes.set_subtype(b, list_sub::V_EXTENSIBLE);
924 if let Some(bd) = self.nodes.box_data_mut(b) {
925 bd.width = width;
926 }
927 b
928 }
929
930 fn extensible_box(&mut self, f: u32, c: u32, v: i32) -> NodeRef {
933 let recipe = self
934 .tfm_of(f)
935 .and_then(|t| {
936 let (_, rem) = self.char_tag(f, c);
937 t.exten.get(rem as usize).copied()
938 })
939 .unwrap_or([0, 0, 0, 0]);
940 let [top, mid, bot, rep] = recipe.map(u32::from);
941 let hd = |e: &Engine, ch: u32| {
942 e.fonts
943 .char_metrics(f, ch)
944 .map_or(0, |m| m.height + m.depth)
945 };
946 let b = self.new_null_box_node(false);
947 let u = hd(self, rep);
948 let mut w = 0;
949 let rm = self.fonts.char_metrics(f, rep).copied().unwrap_or_default();
950 let width = rm.width + rm.italic;
951 if bot != 0 {
952 w += hd(self, bot);
953 }
954 if mid != 0 {
955 w += hd(self, mid);
956 }
957 if top != 0 {
958 w += hd(self, top);
959 }
960 let mut n = 0;
961 if u > 0 {
962 while w < v {
963 w += u;
964 n += 1;
965 if mid != 0 {
966 w += u;
967 }
968 }
969 }
970 if let Some(bd) = self.nodes.box_data_mut(b) {
971 bd.width = width;
972 }
973 if bot != 0 {
974 self.stack_into_box(b, f, bot);
975 }
976 for _ in 0..n {
977 self.stack_into_box(b, f, rep);
978 }
979 if mid != 0 {
980 self.stack_into_box(b, f, mid);
981 for _ in 0..n {
982 self.stack_into_box(b, f, rep);
983 }
984 }
985 if top != 0 {
986 self.stack_into_box(b, f, top);
987 }
988 if let Some(bd) = self.nodes.box_data_mut(b) {
991 bd.height = w;
992 bd.depth = 0;
993 }
994 self.nodes.set_subtype(b, list_sub::V_EXTENSIBLE);
995 b
996 }
997
998 fn stack_into_box(&mut self, b: NodeRef, f: u32, c: u32) {
999 let p = self.char_box(f, c);
1000 let head = self.nodes.list_ptr(b);
1001 self.nodes.link(p, head);
1002 self.nodes.set_list_ptr(b, p);
1003 let h = self.nodes.box_data(p).map_or(0, |x| x.height);
1004 if let Some(bd) = self.nodes.box_data_mut(b) {
1005 bd.height = h;
1006 }
1007 }
1008
1009 fn take_field(&mut self, q: NodeRef, f: Field) -> NodeRef {
1012 let k = self.noad_field(q, f);
1013 self.set_noad_field(q, f, NodeRef::NULL);
1014 k
1015 }
1016
1017 fn set_nucleus_box(&mut self, q: NodeRef, b: NodeRef) {
1019 let old = self.take_field(q, Field::Nucleus);
1020 self.nodes.flush_list(old);
1021 let k = self.new_sub_list(false, b);
1022 self.set_noad_field(q, Field::Nucleus, k);
1023 }
1024
1025 fn make_over(&mut self, q: NodeRef) {
1026 let k = self.noad_field(q, Field::Nucleus);
1027 let st = cramped_style(self.ts.math.style);
1028 let x = self.clean_box(k, st, list_sub::NUCLEUS);
1029 let saved = self.attr_from(k);
1031 let b = self.overbar(
1032 x,
1033 self.math_par(mp::OVERBAR_VGAP),
1034 self.math_par(mp::OVERBAR_RULE),
1035 self.math_par(mp::OVERBAR_KERN),
1036 );
1037 self.attr_restore(saved);
1038 self.nodes.set_subtype(b, list_sub::OVER);
1039 self.set_nucleus_box(q, b);
1040 }
1041
1042 fn make_under(&mut self, q: NodeRef) {
1043 let k = self.noad_field(q, Field::Nucleus);
1044 let x = self.clean_box(k, self.ts.math.style, list_sub::NUCLEUS);
1045 let p = self.new_kern_node(self.math_par(mp::UNDERBAR_VGAP), kern_sub::FONT);
1046 self.nodes.link(x, p);
1047 let r = self.fraction_rule(self.math_par(mp::UNDERBAR_RULE));
1048 self.nodes.link(p, r);
1049 let y = self.vpack(x, PackSpec::NATURAL);
1050 self.nodes.set_subtype(y, list_sub::UNDER);
1051 let (yh, yd) = self
1052 .nodes
1053 .box_data(y)
1054 .map_or((0, 0), |b| (b.height, b.depth));
1055 let xh = self.nodes.box_data(x).map_or(0, |b| b.height);
1056 let delta = yh + yd + self.math_par(mp::UNDERBAR_KERN);
1057 if let Some(b) = self.nodes.box_data_mut(y) {
1058 b.height = xh;
1059 b.depth = delta - xh;
1060 }
1061 self.set_nucleus_box(q, y);
1062 }
1063
1064 fn make_vcenter(&mut self, q: NodeRef) {
1065 let k = self.noad_field(q, Field::Nucleus);
1066 let v = self.nodes.list_ptr_kernel(k);
1067 if v.is_null() || self.nodes.id(v) != id::VLIST {
1068 self.confusion("vcenter");
1069 }
1070 let axis = self.math_par(mp::AXIS);
1071 if let Some(b) = self.nodes.box_data_mut(v) {
1072 let delta = b.height + b.depth;
1073 b.height = axis + half(delta);
1074 b.depth = delta - b.height;
1075 }
1076 }
1077
1078 fn make_radical(&mut self, q: NodeRef) {
1079 let sub = self.nodes.subtype(q);
1080 match sub {
1081 radical_sub::UUNDERDELIMITER
1082 | radical_sub::UOVERDELIMITER
1083 | radical_sub::UDELIMITERUNDER
1084 | radical_sub::UDELIMITEROVER
1085 | radical_sub::UHEXTENSIBLE => {
1086 self.make_delimiter_radical(q, sub);
1087 return;
1088 }
1089 _ => {}
1090 }
1091 let k = self.noad_field(q, Field::Nucleus);
1092 let x = self.clean_box(k, cramped_style(self.ts.math.style), list_sub::NUCLEUS);
1093 let (xh, xd) = self
1094 .nodes
1095 .box_data(x)
1096 .map_or((0, 0), |b| (b.height, b.depth));
1097 let mut clr = self.math_par(mp::RADICAL_VGAP);
1098 let theta = self.math_par_opt(mp::RADICAL_RULE);
1099 let kern = self.math_par(mp::RADICAL_KERN);
1100 let t = theta.unwrap_or(self.math_par(mp::FRACTION_RULE));
1101 let left = self.nodes.noad(q).map_or(NodeRef::NULL, |n| n.left);
1102 let size = self.ts.math.size;
1103 let y = self.var_delimiter(left, size, xh + xd + clr + t, true);
1104 let (yh, yd) = self
1105 .nodes
1106 .box_data(y)
1107 .map_or((0, 0), |b| (b.height, b.depth));
1108 let theta = if let Some(th) = theta {
1109 let delta = yh + yd - th - (xh + xd + clr);
1112 if delta > 0 {
1113 clr += half(delta);
1114 }
1115 if let Some(b) = self.nodes.box_data_mut(y) {
1116 b.shift = yh - (xh + clr + th);
1117 }
1118 th
1119 } else {
1120 let top_h = if self.nodes.id(y) == id::VLIST {
1124 let h = self.nodes.list_ptr(y);
1125 self.nodes.box_data(h).map_or(yh, |b| b.height)
1126 } else {
1127 yh
1128 };
1129 let eq_depth = yh + yd - top_h;
1130 let delta = eq_depth - (xh + xd + clr);
1131 if delta > 0 {
1132 clr += half(delta);
1133 }
1134 if let Some(b) = self.nodes.box_data_mut(y) {
1135 b.shift = -(xh + clr) + (yh - top_h);
1136 }
1137 top_h
1138 };
1139 let saved = self.attr_from(left);
1144 let ob = self.overbar(x, clr, theta, kern);
1145 self.attr_restore(saved);
1146 self.nodes.set_subtype(ob, list_sub::RADICAL);
1147 self.nodes.link(y, ob);
1148 let deg = self.noad_field(q, Field::Degree);
1149 let list = if deg.is_null() {
1150 y
1151 } else {
1152 let r = self.clean_box(deg, 6, list_sub::DEGREE);
1154 let (rw, rh, rd) = self
1155 .nodes
1156 .box_data(r)
1157 .map_or((0, 0, 0), |b| (b.width, b.height, b.depth));
1158 let _ = rd;
1159 let before = self.math_par(mp::RADICAL_DEGREE_BEFORE);
1160 let after = self.math_par(mp::RADICAL_DEGREE_AFTER);
1161 let raise = self.math_par(mp::RADICAL_DEGREE_RAISE);
1162 let (yh2, yd2, ys) = self
1163 .nodes
1164 .box_data(y)
1165 .map_or((0, 0, 0), |b| (b.height, b.depth, b.shift));
1166 let ytot = yh2 + yd2;
1167 let shift = retex_arith::xn_over_d(ytot, raise, 100).map_or(0, |r| r.quotient);
1168 if self.math_par_opt(mp::RADICAL_RULE).is_some() {
1171 if let Some(b) = self.nodes.box_data_mut(r) {
1172 b.shift = -(shift - (yd2 + ys));
1173 }
1174 } else if let Some(b) = self.nodes.box_data_mut(r) {
1175 b.shift = -(shift - yd2 + b.shift.min(0));
1176 }
1177 let _ = rh;
1178 let saved = self.attr_from(r);
1180 let k1 = self.new_kern_node(before, kern_sub::FONT);
1181 let k2 = self.new_kern_node(after, kern_sub::FONT);
1182 self.attr_restore(saved);
1183 let _ = rw;
1184 self.nodes.link(k1, r);
1185 self.nodes.link(r, k2);
1186 self.nodes.link(k2, y);
1187 k1
1188 };
1189 let b = self.hpack_quiet(list);
1190 self.set_nucleus_box(q, b);
1191 }
1192
1193 fn make_delimiter_radical(&mut self, q: NodeRef, sub: u16) {
1202 let k = self.noad_field(q, Field::Nucleus);
1203 let cs = self.ts.math.style;
1204 let left = self.nodes.noad(q).map_or(NodeRef::NULL, |n| n.left);
1205 let size = self.ts.math.size;
1206 if sub == radical_sub::UHEXTENSIBLE {
1207 let width = self.nodes.noad(q).map_or(0, |n| n.width);
1208 let y = self.h_delimiter(left, size, width);
1209 let b = self.hpack_quiet(y);
1210 self.set_nucleus_box(q, b);
1211 return;
1212 }
1213 let st = match sub {
1214 radical_sub::UOVERDELIMITER => sup_style(cs),
1215 radical_sub::UUNDERDELIMITER => sub_style(cs),
1216 _ => cs,
1217 };
1218 let x = if k.is_null() {
1219 self.new_null_box_node(true)
1220 } else {
1221 self.clean_box(k, st, list_sub::NUCLEUS)
1222 };
1223 let dsize = match sub {
1227 radical_sub::UDELIMITEROVER | radical_sub::UDELIMITERUNDER => (size + 1).min(2),
1228 _ => size,
1229 };
1230 let xw0 = self.nodes.box_data(x).map_or(0, |b| b.width);
1231 let y = self.h_delimiter(left, dsize, xw0);
1232 let dim = |e: &Self, b: NodeRef| {
1233 e.nodes
1234 .box_data(b)
1235 .map_or((0, 0, 0), |b| (b.width, b.height, b.depth))
1236 };
1237 let (xw, xh, xd) = dim(self, x);
1238 let (yw, yh, yd) = dim(self, y);
1239 let w = xw.max(yw);
1240 for b in [x, y] {
1241 if let Some(bd) = self.nodes.box_data_mut(b) {
1242 bd.width = w;
1243 }
1244 }
1245 let over = matches!(
1246 sub,
1247 radical_sub::UOVERDELIMITER | radical_sub::UDELIMITEROVER
1248 );
1249 let (vgap, bgap) = if over {
1250 (
1251 self.math_par(mp::OVER_DELIMITER_VGAP),
1252 self.math_par(mp::OVER_DELIMITER_BGAP),
1253 )
1254 } else {
1255 (
1256 self.math_par(mp::UNDER_DELIMITER_VGAP),
1257 self.math_par(mp::UNDER_DELIMITER_BGAP),
1258 )
1259 };
1260 let (top, bottom, kern) = match sub {
1261 radical_sub::UOVERDELIMITER => (x, y, vgap.max(bgap - yh - xd)),
1262 radical_sub::UUNDERDELIMITER => (y, x, vgap.max(bgap - xh - yd)),
1263 radical_sub::UDELIMITEROVER => (y, x, vgap.max(bgap - xh - (yh + yd)) - yd),
1264 _ => (x, y, vgap.max(bgap - xd - (yh + yd)) - yh),
1265 };
1266 let kk = self.new_kern_node(kern, kern_sub::FONT);
1267 self.nodes.link(top, kk);
1268 self.nodes.link(kk, bottom);
1269 let v = self.vpack(top, PackSpec::NATURAL);
1270 self.nodes.set_subtype(
1271 v,
1272 if over {
1273 list_sub::OVER_DELIMITER
1274 } else {
1275 list_sub::UNDER_DELIMITER
1276 },
1277 );
1278 if matches!(
1281 sub,
1282 radical_sub::UUNDERDELIMITER | radical_sub::UDELIMITERUNDER
1283 ) {
1284 let th = self.nodes.box_data(top).map_or(0, |b| b.height);
1285 if let Some(b) = self.nodes.box_data_mut(v) {
1286 let tot = b.height + b.depth;
1287 b.height = th;
1288 b.depth = tot - th;
1289 }
1290 }
1291 self.set_nucleus_box(q, v);
1292 }
1293
1294 fn h_delimiter(&mut self, d: NodeRef, s: u32, w: i32) -> NodeRef {
1301 let saved = self.attr_from(d);
1302 let (sf, sc, _, _) = self.delim_fields(d);
1303 let f = self.fam_fnt(sf, s);
1304 let b = if f == 0 || self.fonts.char_metrics(f, sc).is_none() {
1305 self.new_null_box_node(true)
1306 } else if !self.is_opentype_math_font(f) {
1307 self.char_box(f, sc)
1308 } else {
1309 let mut c = sc;
1310 let mut guard = 0;
1311 let mut assembly = None;
1312 loop {
1313 guard += 1;
1314 let m = self.fonts.char_metrics(f, c).copied().unwrap_or_default();
1315 if m.width >= w || guard > 256 {
1316 break;
1317 }
1318 if let Some(parts) = self.math_char(f, c).and_then(|mc| mc.horiz.clone()) {
1319 assembly = Some(parts);
1320 break;
1321 }
1322 match self.math_char(f, c).and_then(|mc| mc.next) {
1323 Some(n) if self.fonts.char_metrics(f, n).is_some() => c = n,
1324 _ => break,
1325 }
1326 }
1327 match assembly {
1328 Some(parts) => {
1329 let overlap = self.math_par(mp::CONNECTOR_OVERLAP_MIN);
1330 self.ot_h_extensible_box(f, &parts, w, overlap)
1331 }
1332 None => self.char_box(f, c),
1333 }
1334 };
1335 self.attr_restore(saved);
1336 b
1337 }
1338
1339 fn ot_h_extensible_box(
1342 &mut self,
1343 f: u32,
1344 parts: &[crate::fonts::ExtPart],
1345 w: i32,
1346 overlap: i32,
1347 ) -> NodeRef {
1348 let expand = |n: usize| -> Vec<crate::fonts::ExtPart> {
1349 let mut out = Vec::new();
1350 for p in parts.iter() {
1351 let k = if p.extender { n } else { 1 };
1352 for _ in 0..k {
1353 out.push(*p);
1354 }
1355 }
1356 out
1357 };
1358 let joint = |a: &crate::fonts::ExtPart, b: &crate::fonts::ExtPart| {
1359 let max = a.end.min(b.start).max(0);
1360 (max, overlap.min(max))
1361 };
1362 let largest = |seq: &[crate::fonts::ExtPart]| -> i64 {
1363 let mut t: i64 = seq.iter().map(|p| i64::from(p.advance)).sum();
1364 for win in seq.windows(2) {
1365 t -= i64::from(joint(&win[0], &win[1]).1);
1366 }
1367 t
1368 };
1369 let has_extender = parts.iter().any(|p| p.extender);
1370 let mut n = 0;
1371 let mut seq = expand(n);
1372 while has_extender && largest(&seq) < i64::from(w) && n < 1000 {
1373 n += 1;
1374 seq = expand(n);
1375 }
1376 let mut head = NodeRef::NULL;
1377 let mut tail = NodeRef::NULL;
1378 for (i, p) in seq.iter().enumerate() {
1379 if i > 0 {
1380 let (max, min) = joint(&seq[i - 1], p);
1381 let g = crate::glue::Glue {
1382 width: -max,
1383 stretch: max - min,
1384 ..crate::glue::Glue::ZERO
1385 };
1386 let gn = self.new_glue_node(g, 0);
1387 self.nodes.link(tail, gn);
1388 tail = gn;
1389 }
1390 let b = self.char_box(f, p.code);
1391 if head.is_null() {
1392 head = b;
1393 } else {
1394 self.nodes.link(tail, b);
1395 }
1396 tail = b;
1397 }
1398 let natural = largest(&seq) as i32;
1399 let target = w.max(natural.min(w));
1400 let b = self.hpack(head, PackSpec::Exactly(target));
1401 self.nodes.set_subtype(b, list_sub::H_EXTENSIBLE);
1402 b
1403 }
1404
1405 fn make_math_accent(&mut self, q: NodeRef) {
1407 let acc = self.nodes.noad(q).map_or(NodeRef::NULL, |n| n.accent);
1408 if acc.is_null() {
1409 self.make_bottom_accent(q);
1411 return;
1412 }
1413 let Some((f, mut c, mut i)) = self.fetch(acc) else {
1414 return;
1415 };
1416 let mut s = 0;
1418 let nuc = self.noad_field(q, Field::Nucleus);
1419 let mut nuc_char = None;
1420 if !nuc.is_null()
1421 && self.nodes.id(nuc) == id::MATH_CHAR
1422 && let Some((nf, nc, _)) = self.fetch(nuc)
1423 {
1424 nuc_char = Some((nf, nc));
1425 let sk = self.font(nf).skew_char;
1426 if sk >= 0
1427 && let Some(LigOp::Kern(k)) = self.lig_kern_step(nf, nc, sk as u32)
1428 {
1429 s = k;
1430 }
1431 }
1432 let st = cramped_style(self.ts.math.style);
1433 let mut char_nucleus = !nuc.is_null() && self.nodes.id(nuc) == id::MATH_CHAR;
1434 if nuc_char.is_none()
1435 && self.is_opentype_math_font(f)
1436 && let Some(fc) = self.nested_accent_char(nuc)
1437 {
1438 nuc_char = Some(fc);
1441 char_nucleus = true;
1442 }
1443 let mut x = self.clean_box(nuc, st, list_sub::NUCLEUS);
1444 let (w, mut h) = self
1445 .nodes
1446 .box_data(x)
1447 .map_or((0, 0), |b| (b.width, b.height));
1448 let fixed = matches!(
1450 self.nodes.subtype(q),
1451 crate::nodes::accent_sub::FIXED_TOP | crate::nodes::accent_sub::FIXED_BOTH
1452 );
1453 if !fixed {
1454 loop {
1455 let (tag, rem) = self.char_tag(f, c);
1456 if tag != 2 {
1457 break;
1458 }
1459 let Some(m) = self.fonts.char_metrics(f, rem).copied() else {
1460 break;
1461 };
1462 if m.width > w {
1463 break;
1464 }
1465 c = rem;
1466 i = m;
1467 }
1468 }
1469 let _ = i;
1470 let opentype = self.is_opentype_math_font(f);
1471 let xh = if opentype {
1473 self.font(f)
1474 .math_constants
1475 .as_ref()
1476 .and_then(|m| m.get("AccentBaseHeight").copied())
1477 .unwrap_or_else(|| self.x_height(f))
1478 } else {
1479 self.x_height(f)
1480 };
1481 let mut delta = if h < xh { h } else { xh };
1482 let sup = self.noad_field(q, Field::Sup);
1483 let sub = self.noad_field(q, Field::Sub);
1484 if (!sup.is_null() || !sub.is_null())
1485 && !nuc.is_null()
1486 && self.nodes.id(nuc) == id::MATH_CHAR
1487 {
1488 self.nodes.flush_list(x);
1490 let n = self.new_noad(noad_sub::ORD);
1491 let nk = self.take_field(q, Field::Nucleus);
1492 let a = self.take_field(q, Field::Sup);
1493 let b = self.take_field(q, Field::Sub);
1494 self.set_noad_field(n, Field::Nucleus, nk);
1495 self.set_noad_field(n, Field::Sup, a);
1496 self.set_noad_field(n, Field::Sub, b);
1497 let k = self.new_sub_list(true, n);
1498 self.set_noad_field(q, Field::Nucleus, k);
1499 x = self.clean_box(k, self.ts.math.style, list_sub::NUCLEUS);
1500 let nh = self.nodes.box_data(x).map_or(0, |b| b.height);
1501 delta = delta + nh - h;
1502 h = nh;
1503 }
1504 let saved = self.attr_from(acc);
1506 let y = self.char_box(f, c);
1507 self.attr_restore(saved);
1508 let yw = self.nodes.box_data(y).map_or(0, |b| b.width);
1509 let shift = if opentype {
1510 let a = if char_nucleus {
1514 nuc_char
1515 .and_then(|(nf, nc)| self.math_char(nf, nc).and_then(|m| m.top_accent))
1516 .unwrap_or(half(w))
1517 } else {
1518 half(w)
1519 };
1520 let b = self
1521 .math_char(f, c)
1522 .and_then(|m| m.top_accent)
1523 .unwrap_or(half(yw));
1524 a - b
1525 } else if char_nucleus {
1526 s + half(w - yw)
1529 } else {
1530 s + half(w - yw) + 1
1531 };
1532 if let Some(b) = self.nodes.box_data_mut(y) {
1533 b.shift = shift;
1534 b.width = 0;
1535 }
1536 let p = self.new_kern_node(-delta, kern_sub::FONT);
1537 self.nodes.link(p, x);
1538 self.nodes.link(y, p);
1539 let y = self.vpack(y, PackSpec::NATURAL);
1540 self.nodes.set_subtype(y, list_sub::ACCENT);
1541 let mut xw = self.nodes.box_data(x).map_or(0, |b| b.width);
1542 if opentype
1545 && !self.nodes.next(q).is_null()
1546 && let Some((nf, nc)) = nuc_char
1547 && self.nodes.id(nuc) == id::MATH_CHAR
1548 {
1549 xw += self.fonts.char_metrics(nf, nc).map_or(0, |m| m.italic);
1550 }
1551 let yh = self.nodes.box_data(y).map_or(0, |b| b.height);
1552 if let Some(b) = self.nodes.box_data_mut(y) {
1553 b.width = xw;
1554 }
1555 if yh < h {
1556 let p = self.new_kern_node(h - yh, kern_sub::FONT);
1557 let l = self.nodes.list_ptr(y);
1558 self.nodes.link(p, l);
1559 self.nodes.set_list_ptr(y, p);
1560 if let Some(b) = self.nodes.box_data_mut(y) {
1561 b.height = h;
1562 }
1563 }
1564 self.set_nucleus_box(q, y);
1565 let bot = self.nodes.noad(q).map_or(NodeRef::NULL, |n| n.bot_accent);
1566 if !bot.is_null() {
1567 self.make_bottom_accent(q);
1568 }
1569 }
1570
1571 fn make_bottom_accent(&mut self, q: NodeRef) {
1574 let bot = match self.nodes.noad_mut(q) {
1575 Some(n) => std::mem::replace(&mut n.bot_accent, NodeRef::NULL),
1576 None => return,
1577 };
1578 let Some((f, mut c, _)) = self.fetch(bot) else {
1579 self.nodes.flush_list(bot);
1580 return;
1581 };
1582 let bot_attr = self.attr_of(bot);
1583 self.nodes.flush_list(bot);
1584 let fixed = matches!(
1585 self.nodes.subtype(q),
1586 crate::nodes::accent_sub::FIXED_BOTTOM | crate::nodes::accent_sub::FIXED_BOTH
1587 );
1588 let nuc = self.noad_field(q, Field::Nucleus);
1589 let char_nucleus = !nuc.is_null() && self.nodes.id(nuc) == id::MATH_CHAR;
1590 let nuc_char = if char_nucleus {
1591 self.fetch(nuc).map(|(nf, nc, _)| (nf, nc))
1592 } else {
1593 None
1594 };
1595 let sup = self.noad_field(q, Field::Sup);
1597 let sub = self.noad_field(q, Field::Sub);
1598 let x = if char_nucleus && (!sup.is_null() || !sub.is_null()) {
1599 let n = self.new_noad(noad_sub::ORD);
1600 let nk = self.take_field(q, Field::Nucleus);
1601 let a = self.take_field(q, Field::Sup);
1602 let b = self.take_field(q, Field::Sub);
1603 self.set_noad_field(n, Field::Nucleus, nk);
1604 self.set_noad_field(n, Field::Sup, a);
1605 self.set_noad_field(n, Field::Sub, b);
1606 let k = self.new_sub_list(true, n);
1607 self.set_noad_field(q, Field::Nucleus, k);
1608 self.clean_box(k, self.ts.math.style, list_sub::NUCLEUS)
1609 } else {
1610 self.clean_box(nuc, self.ts.math.style, list_sub::NUCLEUS)
1611 };
1612 let (bw, xh) = self
1613 .nodes
1614 .box_data(x)
1615 .map_or((0, 0), |b| (b.width, b.height));
1616 let w = match nuc_char {
1619 Some((nf, nc)) => self.fonts.char_metrics(nf, nc).map_or(bw, |m| {
1620 if self.is_opentype_math_font(nf) {
1621 m.width
1622 } else {
1623 m.width + m.italic
1624 }
1625 }),
1626 None => bw,
1627 };
1628 if !fixed {
1630 loop {
1631 let (tag, rem) = self.char_tag(f, c);
1632 if tag != 2 {
1633 break;
1634 }
1635 match self.fonts.char_metrics(f, rem) {
1636 Some(m) if m.width <= w => c = rem,
1637 _ => break,
1638 }
1639 }
1640 }
1641 let saved = self.attr_set(bot_attr);
1642 let y = self.char_box(f, c);
1643 self.attr_restore(saved);
1644 let yw = self.nodes.box_data(y).map_or(0, |b| b.width);
1645 let shift = if self.is_opentype_math_font(f) {
1646 let a = nuc_char
1647 .filter(|_| self.noad_field(q, Field::Sup).is_null())
1648 .and_then(|(nf, nc)| self.math_char(nf, nc).and_then(|m| m.top_accent))
1649 .unwrap_or(half(w));
1650 let b = self
1651 .math_char(f, c)
1652 .and_then(|m| m.top_accent)
1653 .unwrap_or(half(yw));
1654 a - b
1655 } else if nuc_char.is_some() {
1656 half(w - yw)
1657 } else {
1658 w / 2 - yw / 2 + 1
1659 };
1660 if let Some(b) = self.nodes.box_data_mut(y) {
1661 b.shift = shift;
1662 b.width = 0;
1663 }
1664 self.nodes.link(x, y);
1665 let v = self.vpack(x, PackSpec::NATURAL);
1668 self.nodes.set_subtype(v, list_sub::ACCENT);
1669 if let Some(b) = self.nodes.box_data_mut(v) {
1670 b.shift = b.height - xh;
1671 b.width = bw;
1672 }
1673 self.set_nucleus_box(q, v);
1674 }
1675
1676 fn make_fraction(&mut self, q: NodeRef) {
1678 let (mut thickness, num, denom, left, right, options) = match self.nodes.data(q) {
1679 NodeData::Fraction(f) => (f.thickness, f.num, f.denom, f.left, f.right, f.options),
1680 _ => return,
1681 };
1682 if thickness == DEFAULT_CODE {
1683 thickness = self.math_par(mp::FRACTION_RULE);
1684 }
1685 let cs = self.ts.math.style;
1686 let mut x = self.clean_box(num, num_style(cs), list_sub::NUMERATOR);
1687 let mut z = self.clean_box(denom, denom_style(cs), list_sub::DENOMINATOR);
1688 let (xw, zw) = (
1689 self.nodes.box_data(x).map_or(0, |b| b.width),
1690 self.nodes.box_data(z).map_or(0, |b| b.width),
1691 );
1692 if xw < zw {
1693 x = self.rebox(x, zw);
1694 } else {
1695 z = self.rebox(z, xw);
1696 }
1697 let (mut shift_up, mut shift_down) = if thickness == 0 {
1698 (
1699 self.math_par(mp::STACK_NUM_UP),
1700 self.math_par(mp::STACK_DENOM_DOWN),
1701 )
1702 } else {
1703 (
1704 self.math_par(mp::FRACTION_NUM_UP),
1705 self.math_par(mp::FRACTION_DENOM_DOWN),
1706 )
1707 };
1708 let (xh, xd) = self
1709 .nodes
1710 .box_data(x)
1711 .map_or((0, 0), |b| (b.height, b.depth));
1712 let (zh, zd) = self
1713 .nodes
1714 .box_data(z)
1715 .map_or((0, 0), |b| (b.height, b.depth));
1716 let axis = self.math_par(mp::AXIS);
1717 let delta;
1718 if thickness == 0 {
1719 let clr = self.math_par(mp::STACK_VGAP);
1720 delta = half(clr - ((shift_up - xd) - (zh - shift_down)));
1721 if delta > 0 {
1722 shift_up += delta;
1723 shift_down += delta;
1724 }
1725 } else {
1726 let exact = options & noad_opt::EXACT == noad_opt::EXACT;
1727 let (clr_n, clr_d) = if exact {
1728 (0, 0)
1729 } else if thickness == self.math_par(mp::FRACTION_RULE) {
1730 (
1731 self.math_par(mp::FRACTION_NUM_VGAP),
1732 self.math_par(mp::FRACTION_DENOM_VGAP),
1733 )
1734 } else if cs < 2 {
1735 (3 * thickness, 3 * thickness)
1736 } else {
1737 (thickness, thickness)
1738 };
1739 delta = half(thickness);
1740 let delta1 = clr_n - ((shift_up - xd) - (axis + delta));
1741 let delta2 = clr_d - ((axis - delta) - (zh - shift_down));
1742 if delta1 > 0 {
1743 shift_up += delta1;
1744 }
1745 if delta2 > 0 {
1746 shift_down += delta2;
1747 }
1748 }
1749 let v = self.new_null_box_node(false);
1751 self.nodes.set_subtype(v, list_sub::FRACTION);
1752 let width = self.nodes.box_data(x).map_or(0, |b| b.width);
1753 if let Some(b) = self.nodes.box_data_mut(v) {
1754 b.height = shift_up + xh;
1755 b.depth = zd + shift_down;
1756 b.width = width;
1757 }
1758 let p;
1759 if thickness == 0 {
1760 p = self.new_kern_node((shift_up - xd) - (zh - shift_down), kern_sub::FONT);
1761 self.nodes.link(p, z);
1762 } else {
1763 let y = self.fraction_rule(thickness);
1764 let p2 = self.new_kern_node((axis - delta) - (zh - shift_down), kern_sub::FONT);
1765 self.nodes.link(y, p2);
1766 self.nodes.link(p2, z);
1767 p = self.new_kern_node((shift_up - xd) - (axis + delta), kern_sub::FONT);
1768 self.nodes.link(p, y);
1769 }
1770 self.nodes.link(x, p);
1771 self.nodes.set_list_ptr(v, x);
1772 let dsize = self.math_par(mp::FRACTION_DEL_SIZE);
1774 let size = self.ts.math.size;
1775 let lx = self.var_delimiter(left, size, dsize, left.is_null());
1779 self.nodes.link(lx, v);
1780 let rz = self.var_delimiter(right, size, dsize, right.is_null());
1781 self.nodes.link(v, rz);
1782 let h = self.hpack_quiet(lx);
1783 self.nodes.set_subtype(h, list_sub::FRACTION);
1784 self.set_new_hlist(q, h);
1785 }
1786
1787 fn make_op(&mut self, q: NodeRef) -> i32 {
1789 let cs = self.ts.math.style;
1790 if self.nodes.subtype(q) == noad_sub::OP_DISPLAY_LIMITS && cs < 2 {
1791 self.nodes.set_subtype(q, noad_sub::OP_LIMITS);
1792 }
1793 let limits = self.nodes.subtype(q) == noad_sub::OP_LIMITS;
1794 let nuc = self.noad_field(q, Field::Nucleus);
1795 let mut delta = 0;
1796 let mut opentype = false;
1797 if !nuc.is_null() && self.nodes.id(nuc) == id::MATH_CHAR {
1798 if let Some((f, c, m)) = self.fetch(nuc) {
1799 let mut m = m;
1800 if cs < 2 && self.is_opentype_math_font(f) {
1801 let target = self.math_par(mp::OPERATOR_SIZE);
1805 let mut c = c;
1806 let mut guard = 0;
1807 while m.height + m.depth < target && guard < 64 {
1808 guard += 1;
1809 let (tag, rem) = self.char_tag(f, c);
1810 let Some(m2) = self
1811 .fonts
1812 .char_metrics(f, rem)
1813 .copied()
1814 .filter(|_| tag == 2)
1815 else {
1816 break;
1817 };
1818 m = m2;
1819 c = rem;
1820 }
1821 if let NodeData::MathChar { chr, .. } = self.nodes.data_mut(nuc) {
1822 *chr = c;
1823 }
1824 } else if cs < 2 {
1825 let (tag, rem) = self.char_tag(f, c);
1826 if tag == 2
1827 && let Some(m2) = self.fonts.char_metrics(f, rem).copied()
1828 {
1829 m = m2;
1830 if let NodeData::MathChar { chr, .. } = self.nodes.data_mut(nuc) {
1831 *chr = rem;
1832 }
1833 }
1834 }
1835 delta = m.italic;
1836 opentype = self.is_opentype_math_font(f);
1837 }
1838 let x = self.clean_box(nuc, cs, list_sub::NUCLEUS);
1839 let has_sub = !self.noad_field(q, Field::Sub).is_null();
1840 let axis = self.math_par(mp::AXIS);
1841 if let Some(b) = self.nodes.box_data_mut(x) {
1842 if has_sub && !limits && !opentype {
1843 b.width -= delta;
1844 }
1845 b.shift = half(b.height - b.depth) - axis;
1846 }
1847 self.set_nucleus_box(q, x);
1848 if opentype && !limits {
1852 if has_sub {
1853 self.ts.math.op_kern = -delta;
1854 } else {
1855 delta = 0;
1856 }
1857 }
1858 }
1859 if limits {
1860 let supk = self.noad_field(q, Field::Sup);
1862 let subk = self.noad_field(q, Field::Sub);
1863 let x = self.clean_box(supk, sup_style(cs), list_sub::SUP);
1864 let nk = self.noad_field(q, Field::Nucleus);
1865 let y = self.clean_box(nk, cs, list_sub::NUCLEUS);
1866 let z = self.clean_box(subk, sub_style(cs), list_sub::SUB);
1867 let v = self.new_null_box_node(false);
1868 self.nodes.set_subtype(v, list_sub::LIMITS);
1869 let wy = self.nodes.box_data(y).map_or(0, |b| b.width);
1870 let wx = self.nodes.box_data(x).map_or(0, |b| b.width);
1871 let wz = self.nodes.box_data(z).map_or(0, |b| b.width);
1872 let wv = wy.max(wx).max(wz);
1873 let x = self.rebox(x, wv);
1874 let y = self.rebox(y, wv);
1875 let z = self.rebox(z, wv);
1876 if let Some(b) = self.nodes.box_data_mut(x) {
1877 b.shift = half(delta);
1878 }
1879 if let Some(b) = self.nodes.box_data_mut(z) {
1880 b.shift = -half(delta);
1881 }
1882 let (yh, yd) = self
1883 .nodes
1884 .box_data(y)
1885 .map_or((0, 0), |b| (b.height, b.depth));
1886 let (mut vh, mut vd) = (yh, yd);
1887 let kern = self.math_par(mp::LIMIT_ABOVE_KERN);
1888 let kern_below = self.math_par(mp::LIMIT_BELOW_KERN);
1889 let head;
1890 if supk.is_null() {
1891 self.nodes.free(x);
1892 head = y;
1893 } else {
1894 let (xh, xd) = self
1895 .nodes
1896 .box_data(x)
1897 .map_or((0, 0), |b| (b.height, b.depth));
1898 let mut shift_up = self.math_par(mp::LIMIT_ABOVE_BGAP) - xd;
1899 let g1 = self.math_par(mp::LIMIT_ABOVE_VGAP);
1900 if shift_up < g1 {
1901 shift_up = g1;
1902 }
1903 let p = self.new_kern_node(shift_up, kern_sub::FONT);
1904 self.nodes.link(p, y);
1905 self.nodes.link(x, p);
1906 let p = self.new_kern_node(kern, kern_sub::FONT);
1907 self.nodes.link(p, x);
1908 head = p;
1909 vh += kern + xh + xd + shift_up;
1910 }
1911 if subk.is_null() {
1912 self.nodes.free(z);
1913 } else {
1914 let (zh, zd) = self
1915 .nodes
1916 .box_data(z)
1917 .map_or((0, 0), |b| (b.height, b.depth));
1918 let mut shift_down = self.math_par(mp::LIMIT_BELOW_BGAP) - zh;
1919 let g2 = self.math_par(mp::LIMIT_BELOW_VGAP);
1920 if shift_down < g2 {
1921 shift_down = g2;
1922 }
1923 let p = self.new_kern_node(shift_down, kern_sub::FONT);
1924 self.nodes.link(y, p);
1925 self.nodes.link(p, z);
1926 let p = self.new_kern_node(kern_below, kern_sub::FONT);
1927 self.nodes.link(z, p);
1928 vd += kern_below + zh + zd + shift_down;
1929 }
1930 if let Some(b) = self.nodes.box_data_mut(v) {
1931 b.width = wv;
1932 b.height = vh;
1933 b.depth = vd;
1934 b.head = head;
1935 }
1936 let a = self.take_field(q, Field::Sup);
1938 let b = self.take_field(q, Field::Sub);
1939 self.nodes.flush_list(a);
1940 self.nodes.flush_list(b);
1941 self.set_new_hlist(q, v);
1942 }
1943 delta
1944 }
1945
1946 fn make_ord(&mut self, q: NodeRef) {
1948 'restart: loop {
1949 if !self.noad_field(q, Field::Sub).is_null()
1950 || !self.noad_field(q, Field::Sup).is_null()
1951 {
1952 return;
1953 }
1954 let nuc = self.noad_field(q, Field::Nucleus);
1955 if nuc.is_null() || self.nodes.id(nuc) != id::MATH_CHAR {
1956 return;
1957 }
1958 let p = self.nodes.next(q);
1959 if p.is_null()
1960 || self.nodes.id(p) != id::NOAD
1961 || !matches!(
1962 self.nodes.subtype(p),
1963 noad_sub::ORD
1964 | noad_sub::OP_DISPLAY_LIMITS
1965 | noad_sub::OP_LIMITS
1966 | noad_sub::OP_NO_LIMITS
1967 | noad_sub::BIN
1968 | noad_sub::REL
1969 | noad_sub::OPEN
1970 | noad_sub::CLOSE
1971 | noad_sub::PUNCT
1972 )
1973 {
1974 return;
1975 }
1976 let pn = self.noad_field(p, Field::Nucleus);
1977 if pn.is_null() || self.nodes.id(pn) != id::MATH_CHAR {
1978 return;
1979 }
1980 let (qfam, pfam, pchr) = match (self.nodes.data(nuc), self.nodes.data(pn)) {
1981 (NodeData::MathChar { fam: a, .. }, NodeData::MathChar { fam: b, chr }) => {
1982 (*a, *b, *chr)
1983 }
1984 _ => return,
1985 };
1986 if qfam != pfam {
1987 return;
1988 }
1989 self.nodes.get_mut(nuc).id = id::MATH_TEXT_CHAR;
1990 let Some((f, c, _)) = self.fetch(nuc) else {
1991 return;
1992 };
1993 let Some(op) = self
1994 .lig_kern_step(f, c, pchr)
1995 .or_else(|| self.base_mode_kern(f, c, pchr))
1996 else {
1997 return;
1998 };
1999 match op {
2000 LigOp::Kern(k) => {
2001 let kn = self.new_kern_node(k, kern_sub::FONT);
2002 let n = self.nodes.next(q);
2003 self.nodes.link(kn, n);
2004 self.nodes.link(q, kn);
2005 return;
2006 }
2007 LigOp::Lig { op, chr } => {
2008 match op {
2009 1 | 5 => self.set_char(nuc, chr),
2010 2 | 6 => self.set_char(pn, chr),
2011 3 | 7 | 11 => {
2012 let r = self.new_noad(noad_sub::ORD);
2013 let k = self.new_math_char(qfam, chr);
2014 if op >= 11 {
2015 self.nodes.get_mut(k).id = id::MATH_TEXT_CHAR;
2016 }
2017 self.set_noad_field(r, Field::Nucleus, k);
2018 self.nodes.link(q, r);
2019 self.nodes.link(r, p);
2020 }
2021 _ => {
2022 let n = self.nodes.next(p);
2023 self.nodes.link(q, n);
2024 if n.is_null() {
2025 self.nodes.set_next(q, NodeRef::NULL);
2026 }
2027 self.set_char(nuc, chr);
2028 let a = self.take_field(p, Field::Sub);
2029 let b = self.take_field(p, Field::Sup);
2030 self.set_noad_field(q, Field::Sub, a);
2031 self.set_noad_field(q, Field::Sup, b);
2032 self.nodes.set_next(p, NodeRef::NULL);
2033 self.nodes.flush_list(p);
2034 }
2035 }
2036 if op > 3 {
2037 return;
2038 }
2039 self.nodes.get_mut(nuc).id = id::MATH_CHAR;
2040 continue 'restart;
2041 }
2042 }
2043 }
2044 }
2045
2046 fn set_char(&mut self, k: NodeRef, c: u32) {
2047 if let NodeData::MathChar { chr, .. } = self.nodes.data_mut(k) {
2048 *chr = c;
2049 }
2050 }
2051
2052 fn convert_nucleus(&mut self, q: NodeRef, delta: i32) {
2055 let mut delta = delta;
2056 let nuc = self.noad_field(q, Field::Nucleus);
2057 let kid = if nuc.is_null() { 0 } else { self.nodes.id(nuc) };
2058 let has_sub = !self.noad_field(q, Field::Sub).is_null();
2059 let p = match kid {
2060 id::MATH_CHAR | id::MATH_TEXT_CHAR => match self.fetch(nuc) {
2061 Some((f, c, m)) => {
2062 delta = m.italic;
2063 let saved = self.attr_from(nuc);
2066 let p = self.new_math_glyph(f, c);
2067 let has_sup = !self.noad_field(q, Field::Sup).is_null();
2068 if self.is_opentype_math_font(f) {
2069 if kid == id::MATH_CHAR && !has_sup && !has_sub {
2074 delta = 0;
2075 }
2076 } else if kid == id::MATH_TEXT_CHAR
2077 && self.font(f).params.get(2).copied().unwrap_or(0) != 0
2078 {
2079 delta = 0;
2080 }
2081 if kid == id::MATH_TEXT_CHAR && self.is_opentype_math_font(f) {
2084 self.attr_restore(saved.clone());
2085 }
2086 if !has_sub && delta != 0 {
2087 let k = self.new_kern_node(delta, kern_sub::ITALIC);
2088 self.nodes.link(p, k);
2089 delta = 0;
2090 }
2091 self.attr_restore(saved);
2092 p
2093 }
2094 None => NodeRef::NULL,
2095 },
2096 id::SUB_BOX => {
2097 let b = self.nodes.list_ptr_kernel(nuc);
2098 self.nodes.set_kernel_list(nuc, NodeRef::NULL);
2099 let k = std::mem::take(&mut self.ts.math.op_kern);
2100 if k != 0 && !b.is_null() {
2101 let kn = self.new_kern_node(k, kern_sub::FONT);
2102 self.nodes.link(b, kn);
2103 }
2104 b
2105 }
2106 id::SUB_MLIST => {
2107 let l = self.nodes.list_ptr_kernel(nuc);
2108 self.nodes.set_kernel_list(nuc, NodeRef::NULL);
2109 let hl = self.clean_box_mlist(l, self.ts.math.style);
2110 let saved = self.attr_from(nuc);
2112 let b = self.hpack_quiet(hl);
2113 self.attr_restore(saved);
2114 b
2115 }
2116 _ => {
2117 if has_sub || !self.noad_field(q, Field::Sup).is_null() {
2119 self.new_null_box_node(true)
2120 } else {
2121 NodeRef::NULL
2122 }
2123 }
2124 };
2125 self.set_new_hlist(q, p);
2126 let has_sup = !self.noad_field(q, Field::Sup).is_null();
2127 if !has_sub && !has_sup {
2128 return;
2129 }
2130 self.make_scripts(q, delta);
2131 }
2132
2133 fn make_scripts(&mut self, q: NodeRef, delta: i32) {
2135 let p = self.get_new_hlist(q);
2136 let cs = self.ts.math.style;
2137 let (mut shift_up, mut shift_down);
2138 if !p.is_null() && self.nodes.is_glyph(p) {
2139 shift_up = 0;
2140 shift_down = 0;
2141 } else {
2142 let (h, d) = self.list_height_depth(p);
2143 shift_up = h - self.math_par(mp::SUP_SHIFT_DROP);
2144 shift_down = d + self.math_par(mp::SUB_SHIFT_DROP);
2145 }
2146 let opts = self.nodes.noad(q).map_or(0, |n| n.options);
2147 let supk = self.take_field(q, Field::Sup);
2148 let subk = self.take_field(q, Field::Sub);
2149 if !p.is_null() && self.nodes.is_glyph(p) {
2150 for k in [supk, subk] {
2153 if !k.is_null() && self.nodes.id(k) == id::MATH_CHAR {
2154 let _ = self.fetch(k);
2155 }
2156 }
2157 }
2158 let script_space = self.math_par(mp::SPACE_AFTER_SCRIPT);
2159 let x;
2160 if supk.is_null() {
2161 let st = if opts & noad_opt::NO_SUB_SCRIPT == noad_opt::NO_SUB_SCRIPT {
2163 cs
2164 } else {
2165 sub_style(cs)
2166 };
2167 x = self.clean_box(subk, st, list_sub::SUB);
2168 self.nodes.flush_list(subk);
2169 let xh = self.nodes.box_data(x).map_or(0, |b| b.height);
2170 if let Some(b) = self.nodes.box_data_mut(x) {
2171 b.width += script_space;
2172 }
2173 let s1 = self.math_par(mp::SUB_SHIFT_DOWN);
2174 if shift_down < s1 {
2175 shift_down = s1;
2176 }
2177 let clr = xh - self.math_par(mp::SUB_TOP_MAX);
2178 if shift_down < clr {
2179 shift_down = clr;
2180 }
2181 if let Some(b) = self.nodes.box_data_mut(x) {
2182 b.shift = shift_down;
2183 }
2184 } else {
2185 let st = if opts & noad_opt::NO_SUPER_SCRIPT == noad_opt::NO_SUPER_SCRIPT {
2186 cs
2187 } else {
2188 sup_style(cs)
2189 };
2190 let sx = self.clean_box(supk, st, list_sub::SUP);
2191 self.nodes.flush_list(supk);
2192 let xd = self.nodes.box_data(sx).map_or(0, |b| b.depth);
2193 if let Some(b) = self.nodes.box_data_mut(sx) {
2194 b.width += script_space;
2195 }
2196 let clr = self.math_par(mp::SUP_SHIFT_UP);
2197 if shift_up < clr {
2198 shift_up = clr;
2199 }
2200 let clr = xd + self.math_par(mp::SUP_BOTTOM_MIN);
2201 if shift_up < clr {
2202 shift_up = clr;
2203 }
2204 if subk.is_null() {
2205 if let Some(b) = self.nodes.box_data_mut(sx) {
2206 b.shift = -shift_up;
2207 }
2208 x = sx;
2209 } else {
2210 let st = if opts & noad_opt::NO_SUB_SCRIPT == noad_opt::NO_SUB_SCRIPT {
2211 cs
2212 } else {
2213 sub_style(cs)
2214 };
2215 let y = self.clean_box(subk, st, list_sub::SUB);
2216 self.nodes.flush_list(subk);
2217 let yh = self.nodes.box_data(y).map_or(0, |b| b.height);
2218 if let Some(b) = self.nodes.box_data_mut(y) {
2219 b.width += script_space;
2220 }
2221 let s2 = self.math_par(mp::SUB_SUP_SHIFT_DOWN);
2222 if shift_down < s2 {
2223 shift_down = s2;
2224 }
2225 let mut clr =
2226 self.math_par(mp::SUB_SUP_VGAP) - ((shift_up - xd) - (yh - shift_down));
2227 if clr > 0 {
2228 shift_down += clr;
2229 clr = self.math_par(mp::SUP_SUB_BOTTOM_MAX) - (shift_up - xd);
2230 if clr > 0 {
2231 shift_up += clr;
2232 shift_down -= clr;
2233 }
2234 }
2235 if let Some(b) = self.nodes.box_data_mut(sx) {
2236 b.shift = delta;
2237 }
2238 let p = self.new_kern_node((shift_up - xd) - (yh - shift_down), kern_sub::FONT);
2239 self.nodes.link(sx, p);
2240 self.nodes.link(p, y);
2241 x = self.vpack(sx, PackSpec::NATURAL);
2242 self.nodes.set_subtype(x, list_sub::SCRIPTS);
2243 if let Some(b) = self.nodes.box_data_mut(x) {
2244 b.shift = shift_down;
2245 }
2246 }
2247 }
2248 let nh = self.get_new_hlist(q);
2249 if nh.is_null() {
2250 self.set_new_hlist(q, x);
2251 } else {
2252 let t = self.nodes.tail_of(nh);
2253 self.nodes.link(t, x);
2254 }
2255 }
2256
2257 fn extensible_fence(
2263 &mut self,
2264 delim: NodeRef,
2265 height: i32,
2266 depth: i32,
2267 options: u32,
2268 axis: i32,
2269 ) -> NodeRef {
2270 let size = self.ts.math.size;
2271 if height == 0 && depth == 0 {
2272 let b = self.var_delimiter(delim, size, 0, false);
2273 if let Some(bd) = self.nodes.box_data_mut(b) {
2274 bd.shift = 0;
2275 }
2276 return b;
2277 }
2278 let up = if options & noad_opt::AXIS == noad_opt::AXIS {
2279 axis
2280 } else {
2281 0
2282 };
2283 let b = self.var_delimiter(delim, size, height + depth, false);
2284 if let Some(bd) = self.nodes.box_data_mut(b) {
2285 bd.shift = half(bd.height - bd.depth) - up;
2286 }
2287 let w = self.hpack_quiet(b);
2288 if options & noad_opt::EXACT != noad_opt::EXACT
2289 && let Some(bd) = self.nodes.box_data_mut(w)
2290 {
2291 bd.height = height + up;
2292 bd.depth = depth - up;
2293 }
2294 w
2295 }
2296
2297 fn make_left_right(&mut self, q: NodeRef, style: i32, max_d: i32, max_h: i32) -> NoadType {
2299 self.set_cur_style(style);
2300 let (delim, height, depth, options) = match self.nodes.data(q) {
2301 NodeData::Fence(f) => (f.delim, f.height, f.depth, f.options),
2302 _ => (NodeRef::NULL, 0, 0, 0),
2303 };
2304 let axis = self.math_par(mp::AXIS);
2305 if self.nodes.subtype(q) == fence_sub::NO {
2306 let b = self.extensible_fence(delim, height, depth, options, axis);
2307 self.set_new_hlist(q, b);
2308 return NoadType::Close;
2309 }
2310 let b = if height != 0 || depth != 0 {
2311 let total = height + depth;
2312 let b = self.var_delimiter(delim, self.ts.math.size, total, true);
2313 if options & noad_opt::NO_AXIS == noad_opt::NO_AXIS
2314 && let Some(bd) = self.nodes.box_data_mut(b)
2315 {
2316 bd.shift = 0;
2317 }
2318 b
2319 } else {
2320 let mut delta2 = max_d + axis;
2321 let mut delta1 = max_h + max_d - delta2;
2322 if delta2 > delta1 {
2323 delta1 = delta2;
2324 }
2325 let mut delta = (delta1 / 500) * self.eqtb.int(ip::DELIMITERFACTOR);
2326 delta2 = delta1 + delta1 - self.eqtb.dimen(dp::DELIMITERSHORTFALL);
2327 if delta < delta2 {
2328 delta = delta2;
2329 }
2330 self.var_delimiter(delim, self.ts.math.size, delta, false)
2331 };
2332 self.set_new_hlist(q, b);
2333 if self.nodes.subtype(q) == fence_sub::LEFT {
2334 NoadType::Open
2335 } else {
2336 NoadType::Close
2337 }
2338 }
2339
2340 fn second_pass(
2343 &mut self,
2344 mlist: NodeRef,
2345 style: i32,
2346 penalties: bool,
2347 max_h: i32,
2348 max_d: i32,
2349 ) -> NodeRef {
2350 let head = self.nodes.new_temp();
2351 let mut p = head;
2352 let mut q = mlist;
2353 let mut r_type: Option<NoadType> = None;
2354 self.set_cur_style(style);
2355 let binpen = self.eqtb.int(ip::BINOPPENALTY);
2356 let relpen = self.eqtb.int(ip::RELPENALTY);
2357 while !q.is_null() {
2358 let next = self.nodes.next(q);
2359 let Some(nt) = self.noad_type(q) else {
2360 if let NodeData::Style { style } = self.nodes.data(q) {
2361 let s = i32::from(*style);
2362 self.set_cur_style(s);
2363 self.nodes.free(q);
2364 } else {
2365 self.nodes.link(p, q);
2366 self.nodes.set_next(q, NodeRef::NULL);
2367 p = q;
2368 }
2369 q = next;
2370 continue;
2371 };
2372 let mut t = NoadType::Ord;
2373 let mut pen = 10000;
2374 match nt {
2375 NoadType::Op
2376 | NoadType::Open
2377 | NoadType::Close
2378 | NoadType::Punct
2379 | NoadType::Inner => t = nt,
2380 NoadType::Bin => {
2381 t = NoadType::Bin;
2382 pen = binpen;
2383 }
2384 NoadType::Rel => {
2385 t = NoadType::Rel;
2386 pen = relpen;
2387 }
2388 NoadType::Fraction => t = NoadType::Inner,
2389 NoadType::Left | NoadType::Right => {
2390 let saved = self.attr_from(q);
2391 t = self.make_left_right(q, style, max_d, max_h);
2392 self.attr_restore(saved);
2393 }
2394 _ => {}
2395 }
2396 if let Some(rt) = r_type {
2398 let style = self.ts.math.style as u32;
2399 if let Some((g, sub)) = self.math_spacing(rt as u32, t as u32, style) {
2400 let y = self.math_glue(g, self.ts.math.mu);
2401 let saved = self.attr_from(p);
2404 let z = self.new_glue_node(y, sub);
2405 self.attr_restore(saved);
2406 self.nodes.link(p, z);
2407 p = z;
2408 }
2409 }
2410 let nh = self.get_new_hlist(q);
2412 self.set_new_hlist(q, NodeRef::NULL);
2413 if !nh.is_null() {
2414 self.nodes.link(p, nh);
2415 p = self.nodes.tail_of(nh);
2416 }
2417 if penalties && !next.is_null() && pen < 10000 {
2418 let nid = self.nodes.id(next);
2419 let next_is_rel = self.noad_type(next) == Some(NoadType::Rel);
2420 if nid != id::PENALTY && !next_is_rel {
2421 let saved = self.attr_from(q);
2423 let z = self.new_penalty_node(pen);
2424 self.attr_restore(saved);
2425 self.nodes.set_subtype(z, 5);
2426 self.nodes.link(p, z);
2427 p = z;
2428 }
2429 }
2430 if nt == NoadType::Right {
2431 t = NoadType::Open;
2432 }
2433 r_type = Some(t);
2434 self.nodes.set_next(q, NodeRef::NULL);
2436 self.nodes.flush_list(q);
2437 q = next;
2438 }
2439 let first = self.nodes.next(head);
2440 self.nodes.free(head);
2441 if !first.is_null() {
2442 self.nodes.set_prev(first, NodeRef::NULL);
2443 }
2444 first
2445 }
2446}
2447
2448#[derive(Clone, Copy, PartialEq, Eq)]
2449enum Action {
2450 Convert,
2451 CheckDimensions,
2452 DoneWithNoad,
2453}