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 {
528 let g = self.new_char_node(f, c);
529 self.nodes.set_subtype(g, PROTECTED_GLYPH);
530 if let NodeData::Glyph(d) = &mut self.nodes.get_mut(g).data {
531 d.lang = 0;
532 d.left = 0;
533 d.right = 0;
534 d.uchyph = false;
535 }
536 g
537 }
538
539 fn char_box(&mut self, f: u32, c: u32) -> NodeRef {
542 let m = self.fonts.char_metrics(f, c).copied().unwrap_or_default();
543 let italic = if self.is_opentype_math_font(f) && !self.math_124 {
546 0
547 } else {
548 m.italic
549 };
550 let b = self.new_null_box_node(true);
551 let g = self.new_math_glyph(f, c);
552 if let Some(bd) = self.nodes.box_data_mut(b) {
553 bd.width = m.width + italic;
554 bd.height = m.height;
555 bd.depth = m.depth;
556 bd.head = g;
557 }
558 self.nodes.set_subtype(b, list_sub::MATH_CHAR);
559 b
560 }
561
562 fn char_tag(&self, f: u32, c: u32) -> (u8, u32) {
565 if let Some(t) = self.tfm_of(f)
566 && let Some(ci) = t.char_info(c)
567 {
568 return (ci.tag, u32::from(ci.remainder));
569 }
570 if let Some(m) = self.math_char(f, c) {
571 if let Some(n) = m.next {
572 return (2, n);
573 }
574 if m.vert.is_some() {
575 return (3, c);
576 }
577 }
578 (0, 0)
579 }
580
581 fn clean_box(&mut self, k: NodeRef, s: i32, sub: u16) -> NodeRef {
586 let mut q;
587 let kid = if k.is_null() { 0 } else { self.nodes.id(k) };
588 match kid {
589 id::MATH_CHAR | id::MATH_TEXT_CHAR => {
590 let n = self.new_noad(noad_sub::ORD);
591 let kc = self.nodes.copy_node(k);
592 self.set_noad_field(n, Field::Nucleus, kc);
593 q = self.clean_box_mlist(n, s);
594 }
595 id::SUB_BOX => {
596 q = self.nodes.list_ptr_kernel(k);
597 self.nodes.set_kernel_list(k, NodeRef::NULL);
598 }
599 id::SUB_MLIST => {
600 let l = self.nodes.list_ptr_kernel(k);
601 self.nodes.set_kernel_list(k, NodeRef::NULL);
602 q = self.clean_box_mlist(l, s);
603 }
604 _ => {
605 q = self.new_null_box_node(true);
606 }
607 }
608 let x = if q.is_null() {
611 self.hpack_quiet(q)
612 } else if !self.nodes.is_glyph(q)
613 && self.nodes.next(q).is_null()
614 && matches!(self.nodes.id(q), id::HLIST | id::VLIST)
615 && self.nodes.box_data(q).is_some_and(|b| b.shift == 0)
616 {
617 q
618 } else {
619 let saved = self.attr_from(q);
620 let x = self.hpack_quiet(q);
621 self.attr_restore(saved);
622 x
623 };
624 self.nodes.set_subtype(x, sub);
625 q = self.nodes.list_ptr(x);
627 if !q.is_null() && self.nodes.is_glyph(q) {
628 let r = self.nodes.next(q);
629 if !r.is_null() && self.nodes.next(r).is_null() && self.nodes.id(r) == id::KERN {
630 self.nodes.free(r);
631 self.nodes.set_next(q, NodeRef::NULL);
632 }
633 }
634 x
635 }
636
637 fn clean_box_mlist(&mut self, l: NodeRef, s: i32) -> NodeRef {
638 let saved = self.ts.math;
639 let r = self.mlist_to_hlist_rec(l, s, false);
640 self.ts.math = saved;
641 r
642 }
643
644 fn rebox(&mut self, b: NodeRef, w: i32) -> NodeRef {
646 let bw = self.nodes.box_data(b).map_or(0, |x| x.width);
647 let lp = self.nodes.list_ptr(b);
648 if bw != w && !lp.is_null() {
649 let saved = self.attr_from(b);
651 let mut b = b;
652 if self.nodes.id(b) == id::VLIST {
653 b = self.hpack_quiet(b);
654 }
655 let mut p = self.nodes.list_ptr(b);
656 let bw = self.nodes.box_data(b).map_or(0, |x| x.width);
657 if self.nodes.is_glyph(p) && self.nodes.next(p).is_null() {
658 let (f, c) = {
659 let g = self.nodes.glyph(p).expect("glyph");
660 (g.font, g.chr)
661 };
662 let v = self.fonts.char_metrics(f, c).map_or(0, |m| m.width);
663 if v != bw {
664 let k = self.new_kern_node(bw - v, kern_sub::FONT);
665 self.nodes.link(p, k);
666 }
667 }
668 self.nodes.set_list_ptr(b, NodeRef::NULL);
669 self.nodes.free(b);
670 let ss = crate::glue::Glue {
671 stretch: retex_arith::UNITY,
672 stretch_order: crate::glue::FIL,
673 shrink: retex_arith::UNITY,
674 shrink_order: crate::glue::FIL,
675 ..crate::glue::Glue::ZERO
676 };
677 let g1 = self.new_glue_node(ss, glue_sub::USER);
678 self.nodes.link(g1, p);
679 while !self.nodes.next(p).is_null() {
680 p = self.nodes.next(p);
681 }
682 let g2 = self.new_glue_node(ss, glue_sub::USER);
683 self.nodes.link(p, g2);
684 let r = self.hpack(g1, PackSpec::Exactly(w));
685 self.attr_restore(saved);
686 r
687 } else {
688 if let Some(x) = self.nodes.box_data_mut(b) {
689 x.width = w;
690 }
691 b
692 }
693 }
694
695 fn fraction_rule(&mut self, t: i32) -> NodeRef {
697 self.new_node(
698 id::RULE,
699 0,
700 NodeData::Rule {
701 width: crate::typeset::NULL_FLAG,
702 depth: 0,
703 height: t,
704 dir: 0,
705 index: 0,
706 left: 0,
707 right: 0,
708 },
709 )
710 }
711
712 fn overbar(&mut self, b: NodeRef, k: i32, t: i32, top: i32) -> NodeRef {
714 let p = self.new_kern_node(k, kern_sub::FONT);
715 self.nodes.link(p, b);
716 let q = self.fraction_rule(t);
717 self.nodes.link(q, p);
718 let p = self.new_kern_node(top, kern_sub::FONT);
719 self.nodes.link(p, q);
720 self.vpack(p, PackSpec::NATURAL)
721 }
722
723 pub fn var_delimiter(&mut self, d: NodeRef, s: u32, v: i32, shift: bool) -> NodeRef {
731 let saved = self.attr_from(d);
732 let b = self.var_delimiter_box(d, s, v, shift);
733 self.attr_restore(saved);
734 b
735 }
736
737 fn var_delimiter_box(&mut self, d: NodeRef, s: u32, v: i32, shift: bool) -> NodeRef {
738 let (sf, sc, lf, lc) = self.delim_fields(d);
739 let mut best: Option<(u32, u32, bool)> = None;
740 let mut w = 0i32;
741 'search: for (z0, x0) in [(sf, sc), (lf, lc)] {
742 if z0 == 0 && x0 == 0 {
743 continue;
744 }
745 let mut z = s as i32;
750 while z == s as i32 {
751 let g = self.fam_fnt(z0, z as u32);
752 if g != 0 {
753 let mut y = x0;
754 let mut guard = 0;
755 loop {
756 guard += 1;
757 if guard > 256 {
758 break;
759 }
760 let Some(m) = self.fonts.char_metrics(g, y).copied() else {
761 break;
762 };
763 let (tag, rem) = self.char_tag(g, y);
764 let opentype = self.is_opentype_math_font(g);
769 if tag == 3 && !opentype {
770 best = Some((g, y, true));
771 break 'search;
772 }
773 let u = m.height + m.depth;
774 if u > w {
775 best = Some((g, y, false));
776 w = u;
777 if u >= v {
778 break 'search;
779 }
780 }
781 if opentype && tag != 2 && self.vert_parts(g, y).is_some() {
782 best = Some((g, y, true));
783 break 'search;
784 }
785 if tag == 2 {
786 y = rem;
787 } else {
788 break;
789 }
790 }
791 }
792 z -= 1;
793 }
794 }
795 let b = match best {
796 Some((f, c, true)) if self.is_opentype_math_font(f) => {
797 let saved = self.ts.math;
798 self.set_cur_style(if s == 0 { 2 } else { 2 * s as i32 + 2 });
799 let overlap = self.math_par(mp::CONNECTOR_OVERLAP_MIN);
800 self.ts.math = saved;
801 self.ot_extensible_box(f, c, v, overlap)
802 }
803 Some((f, c, true)) => self.extensible_box(f, c, v),
804 Some((f, c, false)) => self.char_box(f, c),
805 None => {
806 let b = self.new_null_box_node(true);
807 let nds = self.eqtb.dimen(dp::NULLDELIMITERSPACE);
808 if let Some(bd) = self.nodes.box_data_mut(b) {
809 bd.width = nds;
810 }
811 self.nodes.set_subtype(b, list_sub::V_DELIMITER);
812 if !shift {
813 return b;
814 }
815 b
816 }
817 };
818 let saved = self.ts.math;
819 self.set_cur_style(if s == 0 { 2 } else { 2 * s as i32 + 2 });
820 let axis = self.math_par(mp::AXIS);
821 self.ts.math = saved;
822 if let Some(bd) = self.nodes.box_data_mut(b) {
823 bd.shift = half(bd.height - bd.depth) - axis;
824 }
825 b
826 }
827
828 fn nested_accent_char(&mut self, k: NodeRef) -> Option<(u32, u32)> {
831 if k.is_null() || self.nodes.id(k) != id::SUB_MLIST {
832 return None;
833 }
834 let l = self.nodes.list_ptr_kernel(k);
835 if l.is_null()
836 || !self.nodes.next(l).is_null()
837 || self.noad_type(l) != Some(NoadType::Accent)
838 {
839 return None;
840 }
841 let n = self.noad_field(l, Field::Nucleus);
842 if n.is_null() {
843 return None;
844 }
845 if self.nodes.id(n) == id::MATH_CHAR {
846 return self.fetch(n).map(|(f, c, _)| (f, c));
847 }
848 self.nested_accent_char(n)
849 }
850
851 fn vert_parts(&self, f: u32, c: u32) -> Option<std::rc::Rc<[crate::fonts::ExtPart]>> {
853 self.math_char(f, c)?.vert.clone()
854 }
855
856 fn ot_extensible_box(&mut self, f: u32, c: u32, v: i32, overlap: i32) -> NodeRef {
864 let parts = self
865 .vert_parts(f, c)
866 .unwrap_or_else(|| std::rc::Rc::from(Vec::new()));
867 let expand = |n: usize| -> Vec<crate::fonts::ExtPart> {
868 let mut out = Vec::new();
869 for p in parts.iter() {
870 let k = if p.extender { n } else { 1 };
871 for _ in 0..k {
872 out.push(*p);
873 }
874 }
875 out
876 };
877 let joint = |a: &crate::fonts::ExtPart, b: &crate::fonts::ExtPart| {
878 let max = a.end.min(b.start).max(0);
879 (max, overlap.min(max))
880 };
881 let largest = |seq: &[crate::fonts::ExtPart]| -> i64 {
882 let mut t: i64 = seq.iter().map(|p| i64::from(p.advance)).sum();
883 for w in seq.windows(2) {
884 t -= i64::from(joint(&w[0], &w[1]).1);
885 }
886 t
887 };
888 let has_extender = parts.iter().any(|p| p.extender);
889 let mut n = 0;
890 let mut seq = expand(n);
891 while has_extender && largest(&seq) < i64::from(v) && n < 1000 {
892 n += 1;
893 seq = expand(n);
894 }
895 let mut head = NodeRef::NULL;
897 let mut tail = NodeRef::NULL;
898 let mut width = 0;
899 for (i, p) in seq.iter().enumerate().rev() {
900 if i + 1 < seq.len() {
901 let (max, min) = joint(p, &seq[i + 1]);
902 let g = crate::glue::Glue {
903 width: -max,
904 stretch: max - min,
905 ..crate::glue::Glue::ZERO
906 };
907 let gn = self.new_glue_node(g, 0);
908 self.nodes.link(tail, gn);
909 tail = gn;
910 }
911 let m = self
912 .fonts
913 .char_metrics(f, p.code)
914 .copied()
915 .unwrap_or_default();
916 let b = self.new_null_box_node(true);
917 self.nodes.set_subtype(b, list_sub::MATH_CHAR);
918 let gl = self.new_math_glyph(f, p.code);
919 if let Some(bd) = self.nodes.box_data_mut(b) {
923 bd.width = m.width;
924 bd.height = m.height + m.depth;
925 bd.depth = 0;
926 bd.head = gl;
927 }
928 width = width.max(m.width);
929 if head.is_null() {
930 head = b;
931 } else {
932 self.nodes.link(tail, b);
933 }
934 tail = b;
935 }
936 let natural = largest(&seq) as i32;
937 let target = v.max(natural.min(v));
938 let b = self.vpack(head, crate::build::PackSpec::Exactly(target));
939 self.nodes.set_subtype(b, list_sub::V_EXTENSIBLE);
940 if let Some(bd) = self.nodes.box_data_mut(b) {
941 bd.width = width;
942 }
943 b
944 }
945
946 fn extensible_box(&mut self, f: u32, c: u32, v: i32) -> NodeRef {
949 let recipe = self
950 .tfm_of(f)
951 .and_then(|t| {
952 let (_, rem) = self.char_tag(f, c);
953 t.exten.get(rem as usize).copied()
954 })
955 .unwrap_or([0, 0, 0, 0]);
956 let [top, mid, bot, rep] = recipe.map(u32::from);
957 let hd = |e: &Engine, ch: u32| {
958 e.fonts
959 .char_metrics(f, ch)
960 .map_or(0, |m| m.height + m.depth)
961 };
962 let b = self.new_null_box_node(false);
963 let u = hd(self, rep);
964 let mut w = 0;
965 let rm = self.fonts.char_metrics(f, rep).copied().unwrap_or_default();
966 let width = rm.width + rm.italic;
967 if bot != 0 {
968 w += hd(self, bot);
969 }
970 if mid != 0 {
971 w += hd(self, mid);
972 }
973 if top != 0 {
974 w += hd(self, top);
975 }
976 let mut n = 0;
977 if u > 0 {
978 while w < v {
979 w += u;
980 n += 1;
981 if mid != 0 {
982 w += u;
983 }
984 }
985 }
986 if let Some(bd) = self.nodes.box_data_mut(b) {
987 bd.width = width;
988 }
989 if bot != 0 {
990 self.stack_into_box(b, f, bot);
991 }
992 for _ in 0..n {
993 self.stack_into_box(b, f, rep);
994 }
995 if mid != 0 {
996 self.stack_into_box(b, f, mid);
997 for _ in 0..n {
998 self.stack_into_box(b, f, rep);
999 }
1000 }
1001 if top != 0 {
1002 self.stack_into_box(b, f, top);
1003 }
1004 if let Some(bd) = self.nodes.box_data_mut(b) {
1007 bd.height = w;
1008 bd.depth = 0;
1009 }
1010 self.nodes.set_subtype(b, list_sub::V_EXTENSIBLE);
1011 b
1012 }
1013
1014 fn stack_into_box(&mut self, b: NodeRef, f: u32, c: u32) {
1015 let p = self.char_box(f, c);
1016 let head = self.nodes.list_ptr(b);
1017 self.nodes.link(p, head);
1018 self.nodes.set_list_ptr(b, p);
1019 let h = self.nodes.box_data(p).map_or(0, |x| x.height);
1020 if let Some(bd) = self.nodes.box_data_mut(b) {
1021 bd.height = h;
1022 }
1023 }
1024
1025 fn take_field(&mut self, q: NodeRef, f: Field) -> NodeRef {
1028 let k = self.noad_field(q, f);
1029 self.set_noad_field(q, f, NodeRef::NULL);
1030 k
1031 }
1032
1033 fn set_nucleus_box(&mut self, q: NodeRef, b: NodeRef) {
1035 let old = self.take_field(q, Field::Nucleus);
1036 self.nodes.flush_list(old);
1037 let k = self.new_sub_list(false, b);
1038 self.set_noad_field(q, Field::Nucleus, k);
1039 }
1040
1041 fn make_over(&mut self, q: NodeRef) {
1042 let k = self.noad_field(q, Field::Nucleus);
1043 let st = cramped_style(self.ts.math.style);
1044 let x = self.clean_box(k, st, list_sub::NUCLEUS);
1045 let saved = self.attr_from(k);
1047 let b = self.overbar(
1048 x,
1049 self.math_par(mp::OVERBAR_VGAP),
1050 self.math_par(mp::OVERBAR_RULE),
1051 self.math_par(mp::OVERBAR_KERN),
1052 );
1053 self.attr_restore(saved);
1054 self.nodes.set_subtype(b, list_sub::OVER);
1055 self.set_nucleus_box(q, b);
1056 }
1057
1058 fn make_under(&mut self, q: NodeRef) {
1059 let k = self.noad_field(q, Field::Nucleus);
1060 let x = self.clean_box(k, self.ts.math.style, list_sub::NUCLEUS);
1061 let p = self.new_kern_node(self.math_par(mp::UNDERBAR_VGAP), kern_sub::FONT);
1062 self.nodes.link(x, p);
1063 let r = self.fraction_rule(self.math_par(mp::UNDERBAR_RULE));
1064 self.nodes.link(p, r);
1065 let y = self.vpack(x, PackSpec::NATURAL);
1066 self.nodes.set_subtype(y, list_sub::UNDER);
1067 let (yh, yd) = self
1068 .nodes
1069 .box_data(y)
1070 .map_or((0, 0), |b| (b.height, b.depth));
1071 let xh = self.nodes.box_data(x).map_or(0, |b| b.height);
1072 let delta = yh + yd + self.math_par(mp::UNDERBAR_KERN);
1073 if let Some(b) = self.nodes.box_data_mut(y) {
1074 b.height = xh;
1075 b.depth = delta - xh;
1076 }
1077 self.set_nucleus_box(q, y);
1078 }
1079
1080 fn make_vcenter(&mut self, q: NodeRef) {
1081 let k = self.noad_field(q, Field::Nucleus);
1082 let v = self.nodes.list_ptr_kernel(k);
1083 if v.is_null() || self.nodes.id(v) != id::VLIST {
1084 self.confusion("vcenter");
1085 }
1086 let axis = self.math_par(mp::AXIS);
1087 if let Some(b) = self.nodes.box_data_mut(v) {
1088 let delta = b.height + b.depth;
1089 b.height = axis + half(delta);
1090 b.depth = delta - b.height;
1091 }
1092 }
1093
1094 fn make_radical(&mut self, q: NodeRef) {
1095 let sub = self.nodes.subtype(q);
1096 match sub {
1097 radical_sub::UUNDERDELIMITER
1098 | radical_sub::UOVERDELIMITER
1099 | radical_sub::UDELIMITERUNDER
1100 | radical_sub::UDELIMITEROVER
1101 | radical_sub::UHEXTENSIBLE => {
1102 self.make_delimiter_radical(q, sub);
1103 return;
1104 }
1105 _ => {}
1106 }
1107 let k = self.noad_field(q, Field::Nucleus);
1108 let x = self.clean_box(k, cramped_style(self.ts.math.style), list_sub::NUCLEUS);
1109 let (xh, xd) = self
1110 .nodes
1111 .box_data(x)
1112 .map_or((0, 0), |b| (b.height, b.depth));
1113 let mut clr = self.math_par(mp::RADICAL_VGAP);
1114 let theta = self.math_par_opt(mp::RADICAL_RULE);
1115 let kern = self.math_par(mp::RADICAL_KERN);
1116 let t = theta.unwrap_or(self.math_par(mp::FRACTION_RULE));
1117 let left = self.nodes.noad(q).map_or(NodeRef::NULL, |n| n.left);
1118 let size = self.ts.math.size;
1119 let y = self.var_delimiter(left, size, xh + xd + clr + t, true);
1120 let (yh, yd) = self
1121 .nodes
1122 .box_data(y)
1123 .map_or((0, 0), |b| (b.height, b.depth));
1124 let theta = if let Some(th) = theta {
1125 let delta = yh + yd - th - (xh + xd + clr);
1128 if delta > 0 {
1129 clr += half(delta);
1130 }
1131 if let Some(b) = self.nodes.box_data_mut(y) {
1132 b.shift = yh - (xh + clr + th);
1133 }
1134 th
1135 } else {
1136 let top_h = if self.nodes.id(y) == id::VLIST {
1140 let h = self.nodes.list_ptr(y);
1141 self.nodes.box_data(h).map_or(yh, |b| b.height)
1142 } else {
1143 yh
1144 };
1145 let eq_depth = yh + yd - top_h;
1146 let delta = eq_depth - (xh + xd + clr);
1147 if delta > 0 {
1148 clr += half(delta);
1149 }
1150 if let Some(b) = self.nodes.box_data_mut(y) {
1151 b.shift = -(xh + clr) + (yh - top_h);
1152 }
1153 top_h
1154 };
1155 let saved = self.attr_from(left);
1160 let ob = self.overbar(x, clr, theta, kern);
1161 self.attr_restore(saved);
1162 self.nodes.set_subtype(ob, list_sub::RADICAL);
1163 self.nodes.link(y, ob);
1164 let deg = self.noad_field(q, Field::Degree);
1165 let list = if deg.is_null() {
1166 y
1167 } else {
1168 let r = self.clean_box(deg, 6, list_sub::DEGREE);
1170 let (rw, rh, rd) = self
1171 .nodes
1172 .box_data(r)
1173 .map_or((0, 0, 0), |b| (b.width, b.height, b.depth));
1174 let _ = rd;
1175 let before = self.math_par(mp::RADICAL_DEGREE_BEFORE);
1176 let after = self.math_par(mp::RADICAL_DEGREE_AFTER);
1177 let raise = self.math_par(mp::RADICAL_DEGREE_RAISE);
1178 let (yh2, yd2, ys) = self
1179 .nodes
1180 .box_data(y)
1181 .map_or((0, 0, 0), |b| (b.height, b.depth, b.shift));
1182 let ytot = yh2 + yd2;
1183 let shift = retex_arith::xn_over_d(ytot, raise, 100).map_or(0, |r| r.quotient);
1184 if self.math_par_opt(mp::RADICAL_RULE).is_some() {
1187 if let Some(b) = self.nodes.box_data_mut(r) {
1188 b.shift = -(shift - (yd2 + ys));
1189 }
1190 } else if let Some(b) = self.nodes.box_data_mut(r) {
1191 b.shift = -(shift - yd2 + b.shift.min(0));
1192 }
1193 let _ = rh;
1194 let saved = self.attr_from(r);
1196 let k1 = self.new_kern_node(before, kern_sub::FONT);
1197 let k2 = self.new_kern_node(after, kern_sub::FONT);
1198 self.attr_restore(saved);
1199 let _ = rw;
1200 self.nodes.link(k1, r);
1201 self.nodes.link(r, k2);
1202 self.nodes.link(k2, y);
1203 k1
1204 };
1205 let b = self.hpack_quiet(list);
1206 self.set_nucleus_box(q, b);
1207 }
1208
1209 fn make_delimiter_radical(&mut self, q: NodeRef, sub: u16) {
1218 let k = self.noad_field(q, Field::Nucleus);
1219 let cs = self.ts.math.style;
1220 let left = self.nodes.noad(q).map_or(NodeRef::NULL, |n| n.left);
1221 let size = self.ts.math.size;
1222 if sub == radical_sub::UHEXTENSIBLE {
1223 let width = self.nodes.noad(q).map_or(0, |n| n.width);
1224 let y = self.h_delimiter(left, size, width);
1225 let b = self.hpack_quiet(y);
1226 self.set_nucleus_box(q, b);
1227 return;
1228 }
1229 let st = match sub {
1230 radical_sub::UOVERDELIMITER => sup_style(cs),
1231 radical_sub::UUNDERDELIMITER => sub_style(cs),
1232 _ => cs,
1233 };
1234 let x = if k.is_null() {
1235 self.new_null_box_node(true)
1236 } else {
1237 self.clean_box(k, st, list_sub::NUCLEUS)
1238 };
1239 let dsize = match sub {
1243 radical_sub::UDELIMITEROVER | radical_sub::UDELIMITERUNDER => (size + 1).min(2),
1244 _ => size,
1245 };
1246 let xw0 = self.nodes.box_data(x).map_or(0, |b| b.width);
1247 let y = self.h_delimiter(left, dsize, xw0);
1248 let dim = |e: &Self, b: NodeRef| {
1249 e.nodes
1250 .box_data(b)
1251 .map_or((0, 0, 0), |b| (b.width, b.height, b.depth))
1252 };
1253 let (xw, xh, xd) = dim(self, x);
1254 let (yw, yh, yd) = dim(self, y);
1255 let w = xw.max(yw);
1256 for b in [x, y] {
1257 if let Some(bd) = self.nodes.box_data_mut(b) {
1258 bd.width = w;
1259 }
1260 }
1261 let over = matches!(
1262 sub,
1263 radical_sub::UOVERDELIMITER | radical_sub::UDELIMITEROVER
1264 );
1265 let (vgap, bgap) = if over {
1266 (
1267 self.math_par(mp::OVER_DELIMITER_VGAP),
1268 self.math_par(mp::OVER_DELIMITER_BGAP),
1269 )
1270 } else {
1271 (
1272 self.math_par(mp::UNDER_DELIMITER_VGAP),
1273 self.math_par(mp::UNDER_DELIMITER_BGAP),
1274 )
1275 };
1276 let (top, bottom, kern) = match sub {
1277 radical_sub::UOVERDELIMITER => (x, y, vgap.max(bgap - yh - xd)),
1278 radical_sub::UUNDERDELIMITER => (y, x, vgap.max(bgap - xh - yd)),
1279 radical_sub::UDELIMITEROVER => (y, x, vgap.max(bgap - xh - (yh + yd)) - yd),
1280 _ => (x, y, vgap.max(bgap - xd - (yh + yd)) - yh),
1281 };
1282 let kk = self.new_kern_node(kern, kern_sub::FONT);
1283 self.nodes.link(top, kk);
1284 self.nodes.link(kk, bottom);
1285 let v = self.vpack(top, PackSpec::NATURAL);
1286 self.nodes.set_subtype(
1287 v,
1288 if over {
1289 list_sub::OVER_DELIMITER
1290 } else {
1291 list_sub::UNDER_DELIMITER
1292 },
1293 );
1294 if matches!(
1297 sub,
1298 radical_sub::UUNDERDELIMITER | radical_sub::UDELIMITERUNDER
1299 ) {
1300 let th = self.nodes.box_data(top).map_or(0, |b| b.height);
1301 if let Some(b) = self.nodes.box_data_mut(v) {
1302 let tot = b.height + b.depth;
1303 b.height = th;
1304 b.depth = tot - th;
1305 }
1306 }
1307 self.set_nucleus_box(q, v);
1308 }
1309
1310 fn h_delimiter(&mut self, d: NodeRef, s: u32, w: i32) -> NodeRef {
1317 let saved = self.attr_from(d);
1318 let (sf, sc, _, _) = self.delim_fields(d);
1319 let f = self.fam_fnt(sf, s);
1320 let b = if f == 0 || self.fonts.char_metrics(f, sc).is_none() {
1321 self.new_null_box_node(true)
1322 } else if !self.is_opentype_math_font(f) {
1323 self.char_box(f, sc)
1324 } else {
1325 let mut c = sc;
1326 let mut guard = 0;
1327 let mut assembly = None;
1328 loop {
1329 guard += 1;
1330 let m = self.fonts.char_metrics(f, c).copied().unwrap_or_default();
1331 if m.width >= w || guard > 256 {
1332 break;
1333 }
1334 if let Some(parts) = self.math_char(f, c).and_then(|mc| mc.horiz.clone()) {
1335 assembly = Some(parts);
1336 break;
1337 }
1338 match self.math_char(f, c).and_then(|mc| mc.next) {
1339 Some(n) if self.fonts.char_metrics(f, n).is_some() => c = n,
1340 _ => break,
1341 }
1342 }
1343 match assembly {
1344 Some(parts) => {
1345 let overlap = self.math_par(mp::CONNECTOR_OVERLAP_MIN);
1346 self.ot_h_extensible_box(f, &parts, w, overlap)
1347 }
1348 None => self.char_box(f, c),
1349 }
1350 };
1351 self.attr_restore(saved);
1352 b
1353 }
1354
1355 fn ot_h_extensible_box(
1358 &mut self,
1359 f: u32,
1360 parts: &[crate::fonts::ExtPart],
1361 w: i32,
1362 overlap: i32,
1363 ) -> NodeRef {
1364 let expand = |n: usize| -> Vec<crate::fonts::ExtPart> {
1365 let mut out = Vec::new();
1366 for p in parts.iter() {
1367 let k = if p.extender { n } else { 1 };
1368 for _ in 0..k {
1369 out.push(*p);
1370 }
1371 }
1372 out
1373 };
1374 let joint = |a: &crate::fonts::ExtPart, b: &crate::fonts::ExtPart| {
1375 let max = a.end.min(b.start).max(0);
1376 (max, overlap.min(max))
1377 };
1378 let largest = |seq: &[crate::fonts::ExtPart]| -> i64 {
1379 let mut t: i64 = seq.iter().map(|p| i64::from(p.advance)).sum();
1380 for win in seq.windows(2) {
1381 t -= i64::from(joint(&win[0], &win[1]).1);
1382 }
1383 t
1384 };
1385 let has_extender = parts.iter().any(|p| p.extender);
1386 let mut n = 0;
1387 let mut seq = expand(n);
1388 while has_extender && largest(&seq) < i64::from(w) && n < 1000 {
1389 n += 1;
1390 seq = expand(n);
1391 }
1392 let mut head = NodeRef::NULL;
1393 let mut tail = NodeRef::NULL;
1394 for (i, p) in seq.iter().enumerate() {
1395 if i > 0 {
1396 let (max, min) = joint(&seq[i - 1], p);
1397 let g = crate::glue::Glue {
1398 width: -max,
1399 stretch: max - min,
1400 ..crate::glue::Glue::ZERO
1401 };
1402 let gn = self.new_glue_node(g, 0);
1403 self.nodes.link(tail, gn);
1404 tail = gn;
1405 }
1406 let b = self.char_box(f, p.code);
1407 if head.is_null() {
1408 head = b;
1409 } else {
1410 self.nodes.link(tail, b);
1411 }
1412 tail = b;
1413 }
1414 let natural = largest(&seq) as i32;
1415 let target = w.max(natural.min(w));
1416 let b = self.hpack(head, PackSpec::Exactly(target));
1417 self.nodes.set_subtype(b, list_sub::H_EXTENSIBLE);
1418 b
1419 }
1420
1421 fn make_math_accent(&mut self, q: NodeRef) {
1423 let acc = self.nodes.noad(q).map_or(NodeRef::NULL, |n| n.accent);
1424 if acc.is_null() {
1425 self.make_bottom_accent(q);
1427 return;
1428 }
1429 let Some((f, mut c, mut i)) = self.fetch(acc) else {
1430 return;
1431 };
1432 let mut s = 0;
1434 let nuc = self.noad_field(q, Field::Nucleus);
1435 let mut nuc_char = None;
1436 if !nuc.is_null()
1437 && self.nodes.id(nuc) == id::MATH_CHAR
1438 && let Some((nf, nc, _)) = self.fetch(nuc)
1439 {
1440 nuc_char = Some((nf, nc));
1441 let sk = self.font(nf).skew_char;
1442 if sk >= 0
1443 && let Some(LigOp::Kern(k)) = self.lig_kern_step(nf, nc, sk as u32)
1444 {
1445 s = k;
1446 }
1447 }
1448 let st = cramped_style(self.ts.math.style);
1449 let mut char_nucleus = !nuc.is_null() && self.nodes.id(nuc) == id::MATH_CHAR;
1450 if nuc_char.is_none()
1451 && self.is_opentype_math_font(f)
1452 && let Some(fc) = self.nested_accent_char(nuc)
1453 {
1454 nuc_char = Some(fc);
1457 char_nucleus = true;
1458 }
1459 let mut x = self.clean_box(nuc, st, list_sub::NUCLEUS);
1460 let (w, mut h) = self
1461 .nodes
1462 .box_data(x)
1463 .map_or((0, 0), |b| (b.width, b.height));
1464 let fixed = matches!(
1466 self.nodes.subtype(q),
1467 crate::nodes::accent_sub::FIXED_TOP | crate::nodes::accent_sub::FIXED_BOTH
1468 );
1469 if !fixed {
1470 loop {
1471 let (tag, rem) = self.char_tag(f, c);
1472 if tag != 2 {
1473 break;
1474 }
1475 let Some(m) = self.fonts.char_metrics(f, rem).copied() else {
1476 break;
1477 };
1478 if m.width > w {
1479 break;
1480 }
1481 c = rem;
1482 i = m;
1483 }
1484 }
1485 let _ = i;
1486 let opentype = self.is_opentype_math_font(f);
1487 let xh = if opentype {
1489 self.font(f)
1490 .math_constants
1491 .as_ref()
1492 .and_then(|m| m.get("AccentBaseHeight").copied())
1493 .unwrap_or_else(|| self.x_height(f))
1494 } else {
1495 self.x_height(f)
1496 };
1497 let mut delta = if h < xh { h } else { xh };
1498 let sup = self.noad_field(q, Field::Sup);
1499 let sub = self.noad_field(q, Field::Sub);
1500 if (!sup.is_null() || !sub.is_null())
1501 && !nuc.is_null()
1502 && self.nodes.id(nuc) == id::MATH_CHAR
1503 {
1504 self.nodes.flush_list(x);
1506 let n = self.new_noad(noad_sub::ORD);
1507 let nk = self.take_field(q, Field::Nucleus);
1508 let a = self.take_field(q, Field::Sup);
1509 let b = self.take_field(q, Field::Sub);
1510 self.set_noad_field(n, Field::Nucleus, nk);
1511 self.set_noad_field(n, Field::Sup, a);
1512 self.set_noad_field(n, Field::Sub, b);
1513 let k = self.new_sub_list(true, n);
1514 self.set_noad_field(q, Field::Nucleus, k);
1515 x = self.clean_box(k, self.ts.math.style, list_sub::NUCLEUS);
1516 let nh = self.nodes.box_data(x).map_or(0, |b| b.height);
1517 delta = delta + nh - h;
1518 h = nh;
1519 }
1520 let saved = self.attr_from(acc);
1522 let y = self.char_box(f, c);
1523 self.attr_restore(saved);
1524 let yw = self.nodes.box_data(y).map_or(0, |b| b.width);
1525 let shift = if opentype {
1526 let a = if char_nucleus {
1530 nuc_char
1531 .and_then(|(nf, nc)| self.math_char(nf, nc).and_then(|m| m.top_accent))
1532 .unwrap_or(half(w))
1533 } else {
1534 half(w)
1535 };
1536 let b = self
1537 .math_char(f, c)
1538 .and_then(|m| m.top_accent)
1539 .unwrap_or(half(yw));
1540 a - b
1541 } else if char_nucleus {
1542 s + half(w - yw)
1545 } else {
1546 s + half(w - yw) + 1
1547 };
1548 if let Some(b) = self.nodes.box_data_mut(y) {
1549 b.shift = shift;
1550 b.width = 0;
1551 }
1552 let p = self.new_kern_node(-delta, kern_sub::FONT);
1553 self.nodes.link(p, x);
1554 self.nodes.link(y, p);
1555 let y = self.vpack(y, PackSpec::NATURAL);
1556 self.nodes.set_subtype(y, list_sub::ACCENT);
1557 let mut xw = self.nodes.box_data(x).map_or(0, |b| b.width);
1558 if opentype
1562 && !self.math_124
1563 && !self.nodes.next(q).is_null()
1564 && let Some((nf, nc)) = nuc_char
1565 && self.nodes.id(nuc) == id::MATH_CHAR
1566 {
1567 xw += self.fonts.char_metrics(nf, nc).map_or(0, |m| m.italic);
1568 }
1569 let yh = self.nodes.box_data(y).map_or(0, |b| b.height);
1570 if let Some(b) = self.nodes.box_data_mut(y) {
1571 b.width = xw;
1572 }
1573 if yh < h {
1574 let p = self.new_kern_node(h - yh, kern_sub::FONT);
1575 let l = self.nodes.list_ptr(y);
1576 self.nodes.link(p, l);
1577 self.nodes.set_list_ptr(y, p);
1578 if let Some(b) = self.nodes.box_data_mut(y) {
1579 b.height = h;
1580 }
1581 }
1582 self.set_nucleus_box(q, y);
1583 let bot = self.nodes.noad(q).map_or(NodeRef::NULL, |n| n.bot_accent);
1584 if !bot.is_null() {
1585 self.make_bottom_accent(q);
1586 }
1587 }
1588
1589 fn make_bottom_accent(&mut self, q: NodeRef) {
1592 let bot = match self.nodes.noad_mut(q) {
1593 Some(n) => std::mem::replace(&mut n.bot_accent, NodeRef::NULL),
1594 None => return,
1595 };
1596 let Some((f, mut c, _)) = self.fetch(bot) else {
1597 self.nodes.flush_list(bot);
1598 return;
1599 };
1600 let bot_attr = self.attr_of(bot);
1601 self.nodes.flush_list(bot);
1602 let fixed = matches!(
1603 self.nodes.subtype(q),
1604 crate::nodes::accent_sub::FIXED_BOTTOM | crate::nodes::accent_sub::FIXED_BOTH
1605 );
1606 let nuc = self.noad_field(q, Field::Nucleus);
1607 let char_nucleus = !nuc.is_null() && self.nodes.id(nuc) == id::MATH_CHAR;
1608 let nuc_char = if char_nucleus {
1609 self.fetch(nuc).map(|(nf, nc, _)| (nf, nc))
1610 } else {
1611 None
1612 };
1613 let sup = self.noad_field(q, Field::Sup);
1615 let sub = self.noad_field(q, Field::Sub);
1616 let x = if char_nucleus && (!sup.is_null() || !sub.is_null()) {
1617 let n = self.new_noad(noad_sub::ORD);
1618 let nk = self.take_field(q, Field::Nucleus);
1619 let a = self.take_field(q, Field::Sup);
1620 let b = self.take_field(q, Field::Sub);
1621 self.set_noad_field(n, Field::Nucleus, nk);
1622 self.set_noad_field(n, Field::Sup, a);
1623 self.set_noad_field(n, Field::Sub, b);
1624 let k = self.new_sub_list(true, n);
1625 self.set_noad_field(q, Field::Nucleus, k);
1626 self.clean_box(k, self.ts.math.style, list_sub::NUCLEUS)
1627 } else {
1628 self.clean_box(nuc, self.ts.math.style, list_sub::NUCLEUS)
1629 };
1630 let (bw, xh) = self
1631 .nodes
1632 .box_data(x)
1633 .map_or((0, 0), |b| (b.width, b.height));
1634 let w = match nuc_char {
1637 Some((nf, nc)) => self.fonts.char_metrics(nf, nc).map_or(bw, |m| {
1638 if self.is_opentype_math_font(nf) {
1639 m.width
1640 } else {
1641 m.width + m.italic
1642 }
1643 }),
1644 None => bw,
1645 };
1646 if !fixed {
1648 loop {
1649 let (tag, rem) = self.char_tag(f, c);
1650 if tag != 2 {
1651 break;
1652 }
1653 match self.fonts.char_metrics(f, rem) {
1654 Some(m) if m.width <= w => c = rem,
1655 _ => break,
1656 }
1657 }
1658 }
1659 let saved = self.attr_set(bot_attr);
1660 let y = self.char_box(f, c);
1661 self.attr_restore(saved);
1662 let yw = self.nodes.box_data(y).map_or(0, |b| b.width);
1663 let shift = if self.is_opentype_math_font(f) {
1664 let a = nuc_char
1665 .filter(|_| self.noad_field(q, Field::Sup).is_null())
1666 .and_then(|(nf, nc)| self.math_char(nf, nc).and_then(|m| m.top_accent))
1667 .unwrap_or(half(w));
1668 let b = self
1669 .math_char(f, c)
1670 .and_then(|m| m.top_accent)
1671 .unwrap_or(half(yw));
1672 a - b
1673 } else if nuc_char.is_some() {
1674 half(w - yw)
1675 } else {
1676 w / 2 - yw / 2 + 1
1677 };
1678 if let Some(b) = self.nodes.box_data_mut(y) {
1679 b.shift = shift;
1680 b.width = 0;
1681 }
1682 self.nodes.link(x, y);
1683 let v = self.vpack(x, PackSpec::NATURAL);
1686 self.nodes.set_subtype(v, list_sub::ACCENT);
1687 if let Some(b) = self.nodes.box_data_mut(v) {
1688 b.shift = b.height - xh;
1689 b.width = bw;
1690 }
1691 self.set_nucleus_box(q, v);
1692 }
1693
1694 fn make_fraction(&mut self, q: NodeRef) {
1696 let (mut thickness, num, denom, left, right, options) = match self.nodes.data(q) {
1697 NodeData::Fraction(f) => (f.thickness, f.num, f.denom, f.left, f.right, f.options),
1698 _ => return,
1699 };
1700 if thickness == DEFAULT_CODE {
1701 thickness = self.math_par(mp::FRACTION_RULE);
1702 }
1703 let cs = self.ts.math.style;
1704 let mut x = self.clean_box(num, num_style(cs), list_sub::NUMERATOR);
1705 let mut z = self.clean_box(denom, denom_style(cs), list_sub::DENOMINATOR);
1706 let (xw, zw) = (
1707 self.nodes.box_data(x).map_or(0, |b| b.width),
1708 self.nodes.box_data(z).map_or(0, |b| b.width),
1709 );
1710 if xw < zw {
1711 x = self.rebox(x, zw);
1712 } else {
1713 z = self.rebox(z, xw);
1714 }
1715 let (mut shift_up, mut shift_down) = if thickness == 0 {
1716 (
1717 self.math_par(mp::STACK_NUM_UP),
1718 self.math_par(mp::STACK_DENOM_DOWN),
1719 )
1720 } else {
1721 (
1722 self.math_par(mp::FRACTION_NUM_UP),
1723 self.math_par(mp::FRACTION_DENOM_DOWN),
1724 )
1725 };
1726 let (xh, xd) = self
1727 .nodes
1728 .box_data(x)
1729 .map_or((0, 0), |b| (b.height, b.depth));
1730 let (zh, zd) = self
1731 .nodes
1732 .box_data(z)
1733 .map_or((0, 0), |b| (b.height, b.depth));
1734 let axis = self.math_par(mp::AXIS);
1735 let delta;
1736 if thickness == 0 {
1737 let clr = self.math_par(mp::STACK_VGAP);
1738 delta = half(clr - ((shift_up - xd) - (zh - shift_down)));
1739 if delta > 0 {
1740 shift_up += delta;
1741 shift_down += delta;
1742 }
1743 } else {
1744 let exact = options & noad_opt::EXACT == noad_opt::EXACT;
1745 let (clr_n, clr_d) = if exact {
1746 (0, 0)
1747 } else if thickness == self.math_par(mp::FRACTION_RULE) {
1748 (
1749 self.math_par(mp::FRACTION_NUM_VGAP),
1750 self.math_par(mp::FRACTION_DENOM_VGAP),
1751 )
1752 } else if cs < 2 {
1753 (3 * thickness, 3 * thickness)
1754 } else {
1755 (thickness, thickness)
1756 };
1757 delta = half(thickness);
1758 let delta1 = clr_n - ((shift_up - xd) - (axis + delta));
1759 let delta2 = clr_d - ((axis - delta) - (zh - shift_down));
1760 if delta1 > 0 {
1761 shift_up += delta1;
1762 }
1763 if delta2 > 0 {
1764 shift_down += delta2;
1765 }
1766 }
1767 let v = self.new_null_box_node(false);
1769 self.nodes.set_subtype(v, list_sub::FRACTION);
1770 let width = self.nodes.box_data(x).map_or(0, |b| b.width);
1771 if let Some(b) = self.nodes.box_data_mut(v) {
1772 b.height = shift_up + xh;
1773 b.depth = zd + shift_down;
1774 b.width = width;
1775 }
1776 let p;
1777 if thickness == 0 {
1778 p = self.new_kern_node((shift_up - xd) - (zh - shift_down), kern_sub::FONT);
1779 self.nodes.link(p, z);
1780 } else {
1781 let y = self.fraction_rule(thickness);
1782 let p2 = self.new_kern_node((axis - delta) - (zh - shift_down), kern_sub::FONT);
1783 self.nodes.link(y, p2);
1784 self.nodes.link(p2, z);
1785 p = self.new_kern_node((shift_up - xd) - (axis + delta), kern_sub::FONT);
1786 self.nodes.link(p, y);
1787 }
1788 self.nodes.link(x, p);
1789 self.nodes.set_list_ptr(v, x);
1790 let dsize = self.math_par(mp::FRACTION_DEL_SIZE);
1792 let size = self.ts.math.size;
1793 let lx = self.var_delimiter(left, size, dsize, left.is_null());
1797 self.nodes.link(lx, v);
1798 let rz = self.var_delimiter(right, size, dsize, right.is_null());
1799 self.nodes.link(v, rz);
1800 let h = self.hpack_quiet(lx);
1801 self.nodes.set_subtype(h, list_sub::FRACTION);
1802 self.set_new_hlist(q, h);
1803 }
1804
1805 fn make_op(&mut self, q: NodeRef) -> i32 {
1807 let cs = self.ts.math.style;
1808 if self.nodes.subtype(q) == noad_sub::OP_DISPLAY_LIMITS && cs < 2 {
1809 self.nodes.set_subtype(q, noad_sub::OP_LIMITS);
1810 }
1811 let limits = self.nodes.subtype(q) == noad_sub::OP_LIMITS;
1812 let nuc = self.noad_field(q, Field::Nucleus);
1813 let mut delta = 0;
1814 let mut opentype = false;
1815 if !nuc.is_null() && self.nodes.id(nuc) == id::MATH_CHAR {
1816 if let Some((f, c, m)) = self.fetch(nuc) {
1817 let mut m = m;
1818 if cs < 2 && self.is_opentype_math_font(f) {
1819 let target = self.math_par(mp::OPERATOR_SIZE);
1823 let mut c = c;
1824 let mut guard = 0;
1825 while m.height + m.depth < target && guard < 64 {
1826 guard += 1;
1827 let (tag, rem) = self.char_tag(f, c);
1828 let Some(m2) = self
1829 .fonts
1830 .char_metrics(f, rem)
1831 .copied()
1832 .filter(|_| tag == 2)
1833 else {
1834 break;
1835 };
1836 m = m2;
1837 c = rem;
1838 }
1839 if let NodeData::MathChar { chr, .. } = self.nodes.data_mut(nuc) {
1840 *chr = c;
1841 }
1842 } else if cs < 2 {
1843 let (tag, rem) = self.char_tag(f, c);
1844 if tag == 2
1845 && let Some(m2) = self.fonts.char_metrics(f, rem).copied()
1846 {
1847 m = m2;
1848 if let NodeData::MathChar { chr, .. } = self.nodes.data_mut(nuc) {
1849 *chr = rem;
1850 }
1851 }
1852 }
1853 delta = m.italic;
1854 opentype = self.is_opentype_math_font(f);
1855 }
1856 let saved = std::mem::replace(&mut self.math_op_nucleus, true);
1857 let x = self.clean_box(nuc, cs, list_sub::NUCLEUS);
1858 self.math_op_nucleus = saved;
1859 let has_sub = !self.noad_field(q, Field::Sub).is_null();
1860 let no_limits = self.nodes.subtype(q) == noad_sub::OP_NO_LIMITS;
1861 let axis = self.math_par(mp::AXIS);
1862 let math_124 = self.math_124;
1863 if let Some(b) = self.nodes.box_data_mut(x) {
1864 if has_sub && !limits && !opentype {
1865 b.width -= delta;
1866 }
1867 if opentype && math_124 {
1868 b.width += delta;
1872 if no_limits {
1873 b.width -= delta;
1874 }
1875 if has_sub && !limits {
1876 b.width -= delta;
1877 }
1878 }
1879 b.shift = half(b.height - b.depth) - axis;
1880 }
1881 self.set_nucleus_box(q, x);
1882 if opentype && !limits {
1887 if !has_sub {
1888 delta = 0;
1889 } else if !math_124 {
1890 self.ts.math.op_kern = -delta;
1891 }
1892 }
1893 }
1894 if limits {
1895 let supk = self.noad_field(q, Field::Sup);
1897 let subk = self.noad_field(q, Field::Sub);
1898 let x = self.clean_box(supk, sup_style(cs), list_sub::SUP);
1899 let nk = self.noad_field(q, Field::Nucleus);
1900 let y = self.clean_box(nk, cs, list_sub::NUCLEUS);
1901 let z = self.clean_box(subk, sub_style(cs), list_sub::SUB);
1902 let v = self.new_null_box_node(false);
1903 self.nodes.set_subtype(v, list_sub::LIMITS);
1904 let wy = self.nodes.box_data(y).map_or(0, |b| b.width);
1905 let wx = self.nodes.box_data(x).map_or(0, |b| b.width);
1906 let wz = self.nodes.box_data(z).map_or(0, |b| b.width);
1907 let wv = wy.max(wx).max(wz);
1908 let x = self.rebox(x, wv);
1909 let y = self.rebox(y, wv);
1910 let z = self.rebox(z, wv);
1911 if let Some(b) = self.nodes.box_data_mut(x) {
1912 b.shift = half(delta);
1913 }
1914 if let Some(b) = self.nodes.box_data_mut(z) {
1915 b.shift = -half(delta);
1916 }
1917 let (yh, yd) = self
1918 .nodes
1919 .box_data(y)
1920 .map_or((0, 0), |b| (b.height, b.depth));
1921 let (mut vh, mut vd) = (yh, yd);
1922 let kern = self.math_par(mp::LIMIT_ABOVE_KERN);
1923 let kern_below = self.math_par(mp::LIMIT_BELOW_KERN);
1924 let head;
1925 if supk.is_null() {
1926 self.nodes.free(x);
1927 head = y;
1928 } else {
1929 let (xh, xd) = self
1930 .nodes
1931 .box_data(x)
1932 .map_or((0, 0), |b| (b.height, b.depth));
1933 let mut shift_up = self.math_par(mp::LIMIT_ABOVE_BGAP) - xd;
1934 let g1 = self.math_par(mp::LIMIT_ABOVE_VGAP);
1935 if shift_up < g1 {
1936 shift_up = g1;
1937 }
1938 let p = self.new_kern_node(shift_up, kern_sub::FONT);
1939 self.nodes.link(p, y);
1940 self.nodes.link(x, p);
1941 let p = self.new_kern_node(kern, kern_sub::FONT);
1942 self.nodes.link(p, x);
1943 head = p;
1944 vh += kern + xh + xd + shift_up;
1945 }
1946 if subk.is_null() {
1947 self.nodes.free(z);
1948 } else {
1949 let (zh, zd) = self
1950 .nodes
1951 .box_data(z)
1952 .map_or((0, 0), |b| (b.height, b.depth));
1953 let mut shift_down = self.math_par(mp::LIMIT_BELOW_BGAP) - zh;
1954 let g2 = self.math_par(mp::LIMIT_BELOW_VGAP);
1955 if shift_down < g2 {
1956 shift_down = g2;
1957 }
1958 let p = self.new_kern_node(shift_down, kern_sub::FONT);
1959 self.nodes.link(y, p);
1960 self.nodes.link(p, z);
1961 let p = self.new_kern_node(kern_below, kern_sub::FONT);
1962 self.nodes.link(z, p);
1963 vd += kern_below + zh + zd + shift_down;
1964 }
1965 if let Some(b) = self.nodes.box_data_mut(v) {
1966 b.width = wv;
1967 b.height = vh;
1968 b.depth = vd;
1969 b.head = head;
1970 }
1971 let a = self.take_field(q, Field::Sup);
1973 let b = self.take_field(q, Field::Sub);
1974 self.nodes.flush_list(a);
1975 self.nodes.flush_list(b);
1976 self.set_new_hlist(q, v);
1977 }
1978 delta
1979 }
1980
1981 fn make_ord(&mut self, q: NodeRef) {
1983 'restart: loop {
1984 if !self.noad_field(q, Field::Sub).is_null()
1985 || !self.noad_field(q, Field::Sup).is_null()
1986 {
1987 return;
1988 }
1989 let nuc = self.noad_field(q, Field::Nucleus);
1990 if nuc.is_null() || self.nodes.id(nuc) != id::MATH_CHAR {
1991 return;
1992 }
1993 let p = self.nodes.next(q);
1994 if p.is_null()
1995 || self.nodes.id(p) != id::NOAD
1996 || !matches!(
1997 self.nodes.subtype(p),
1998 noad_sub::ORD
1999 | noad_sub::OP_DISPLAY_LIMITS
2000 | noad_sub::OP_LIMITS
2001 | noad_sub::OP_NO_LIMITS
2002 | noad_sub::BIN
2003 | noad_sub::REL
2004 | noad_sub::OPEN
2005 | noad_sub::CLOSE
2006 | noad_sub::PUNCT
2007 )
2008 {
2009 return;
2010 }
2011 let pn = self.noad_field(p, Field::Nucleus);
2012 if pn.is_null() || self.nodes.id(pn) != id::MATH_CHAR {
2013 return;
2014 }
2015 let (qfam, pfam, pchr) = match (self.nodes.data(nuc), self.nodes.data(pn)) {
2016 (NodeData::MathChar { fam: a, .. }, NodeData::MathChar { fam: b, chr }) => {
2017 (*a, *b, *chr)
2018 }
2019 _ => return,
2020 };
2021 if qfam != pfam {
2022 return;
2023 }
2024 self.nodes.get_mut(nuc).id = id::MATH_TEXT_CHAR;
2025 let Some((f, c, _)) = self.fetch(nuc) else {
2026 return;
2027 };
2028 let Some(op) = self
2029 .lig_kern_step(f, c, pchr)
2030 .or_else(|| self.base_mode_kern(f, c, pchr))
2031 else {
2032 return;
2033 };
2034 match op {
2035 LigOp::Kern(k) => {
2036 let kn = self.new_kern_node(k, kern_sub::FONT);
2037 let n = self.nodes.next(q);
2038 self.nodes.link(kn, n);
2039 self.nodes.link(q, kn);
2040 return;
2041 }
2042 LigOp::Lig { op, chr } => {
2043 match op {
2044 1 | 5 => self.set_char(nuc, chr),
2045 2 | 6 => self.set_char(pn, chr),
2046 3 | 7 | 11 => {
2047 let r = self.new_noad(noad_sub::ORD);
2048 let k = self.new_math_char(qfam, chr);
2049 if op >= 11 {
2050 self.nodes.get_mut(k).id = id::MATH_TEXT_CHAR;
2051 }
2052 self.set_noad_field(r, Field::Nucleus, k);
2053 self.nodes.link(q, r);
2054 self.nodes.link(r, p);
2055 }
2056 _ => {
2057 let n = self.nodes.next(p);
2058 self.nodes.link(q, n);
2059 if n.is_null() {
2060 self.nodes.set_next(q, NodeRef::NULL);
2061 }
2062 self.set_char(nuc, chr);
2063 let a = self.take_field(p, Field::Sub);
2064 let b = self.take_field(p, Field::Sup);
2065 self.set_noad_field(q, Field::Sub, a);
2066 self.set_noad_field(q, Field::Sup, b);
2067 self.nodes.set_next(p, NodeRef::NULL);
2068 self.nodes.flush_list(p);
2069 }
2070 }
2071 if op > 3 {
2072 return;
2073 }
2074 self.nodes.get_mut(nuc).id = id::MATH_CHAR;
2075 continue 'restart;
2076 }
2077 }
2078 }
2079 }
2080
2081 fn set_char(&mut self, k: NodeRef, c: u32) {
2082 if let NodeData::MathChar { chr, .. } = self.nodes.data_mut(k) {
2083 *chr = c;
2084 }
2085 }
2086
2087 fn convert_nucleus(&mut self, q: NodeRef, delta: i32) {
2090 let mut delta = delta;
2091 let nuc = self.noad_field(q, Field::Nucleus);
2092 let kid = if nuc.is_null() { 0 } else { self.nodes.id(nuc) };
2093 let has_sub = !self.noad_field(q, Field::Sub).is_null();
2094 let p = match kid {
2095 id::MATH_CHAR | id::MATH_TEXT_CHAR => match self.fetch(nuc) {
2096 Some((f, c, m)) => {
2097 delta = m.italic;
2098 let saved = self.attr_from(nuc);
2101 let p = self.new_math_glyph(f, c);
2102 let has_sup = !self.noad_field(q, Field::Sup).is_null();
2103 if self.is_opentype_math_font(f) {
2104 if kid == id::MATH_CHAR
2110 && !has_sup
2111 && !has_sub
2112 && (!self.math_124 || self.math_op_nucleus)
2113 {
2114 delta = 0;
2115 }
2116 } else if kid == id::MATH_TEXT_CHAR
2117 && self.font(f).params.get(2).copied().unwrap_or(0) != 0
2118 && !self.math_124
2119 {
2120 delta = 0;
2124 }
2125 if kid == id::MATH_TEXT_CHAR && self.is_opentype_math_font(f) {
2128 self.attr_restore(saved.clone());
2129 }
2130 if !has_sub && delta != 0 {
2131 let k = self.new_kern_node(delta, kern_sub::ITALIC);
2132 self.nodes.link(p, k);
2133 delta = 0;
2134 }
2135 self.attr_restore(saved);
2136 p
2137 }
2138 None => NodeRef::NULL,
2139 },
2140 id::SUB_BOX => {
2141 let b = self.nodes.list_ptr_kernel(nuc);
2142 self.nodes.set_kernel_list(nuc, NodeRef::NULL);
2143 let k = std::mem::take(&mut self.ts.math.op_kern);
2144 if k != 0 && !b.is_null() {
2145 let kn = self.new_kern_node(k, kern_sub::FONT);
2146 self.nodes.link(b, kn);
2147 }
2148 b
2149 }
2150 id::SUB_MLIST => {
2151 let l = self.nodes.list_ptr_kernel(nuc);
2152 self.nodes.set_kernel_list(nuc, NodeRef::NULL);
2153 let hl = self.clean_box_mlist(l, self.ts.math.style);
2154 let saved = self.attr_from(nuc);
2156 let b = self.hpack_quiet(hl);
2157 self.attr_restore(saved);
2158 b
2159 }
2160 _ => {
2161 if has_sub || !self.noad_field(q, Field::Sup).is_null() {
2163 self.new_null_box_node(true)
2164 } else {
2165 NodeRef::NULL
2166 }
2167 }
2168 };
2169 self.set_new_hlist(q, p);
2170 let has_sup = !self.noad_field(q, Field::Sup).is_null();
2171 if !has_sub && !has_sup {
2172 return;
2173 }
2174 self.make_scripts(q, delta);
2175 }
2176
2177 fn make_scripts(&mut self, q: NodeRef, delta: i32) {
2179 let p = self.get_new_hlist(q);
2180 let cs = self.ts.math.style;
2181 let (mut shift_up, mut shift_down);
2182 if !p.is_null() && self.nodes.is_glyph(p) {
2183 shift_up = 0;
2184 shift_down = 0;
2185 } else {
2186 let (h, d) = self.list_height_depth(p);
2187 shift_up = h - self.math_par(mp::SUP_SHIFT_DROP);
2188 shift_down = d + self.math_par(mp::SUB_SHIFT_DROP);
2189 }
2190 let opts = self.nodes.noad(q).map_or(0, |n| n.options);
2191 let supk = self.take_field(q, Field::Sup);
2192 let subk = self.take_field(q, Field::Sub);
2193 if !p.is_null() && self.nodes.is_glyph(p) {
2194 for k in [supk, subk] {
2197 if !k.is_null() && self.nodes.id(k) == id::MATH_CHAR {
2198 let _ = self.fetch(k);
2199 }
2200 }
2201 }
2202 let script_space = self.math_par(mp::SPACE_AFTER_SCRIPT);
2203 let x;
2204 if supk.is_null() {
2205 let st = if opts & noad_opt::NO_SUB_SCRIPT == noad_opt::NO_SUB_SCRIPT {
2207 cs
2208 } else {
2209 sub_style(cs)
2210 };
2211 x = self.clean_box(subk, st, list_sub::SUB);
2212 self.nodes.flush_list(subk);
2213 let xh = self.nodes.box_data(x).map_or(0, |b| b.height);
2214 if let Some(b) = self.nodes.box_data_mut(x) {
2215 b.width += script_space;
2216 }
2217 let s1 = self.math_par(mp::SUB_SHIFT_DOWN);
2218 if shift_down < s1 {
2219 shift_down = s1;
2220 }
2221 let clr = xh - self.math_par(mp::SUB_TOP_MAX);
2222 if shift_down < clr {
2223 shift_down = clr;
2224 }
2225 if let Some(b) = self.nodes.box_data_mut(x) {
2226 b.shift = shift_down;
2227 }
2228 } else {
2229 let st = if opts & noad_opt::NO_SUPER_SCRIPT == noad_opt::NO_SUPER_SCRIPT {
2230 cs
2231 } else {
2232 sup_style(cs)
2233 };
2234 let sx = self.clean_box(supk, st, list_sub::SUP);
2235 self.nodes.flush_list(supk);
2236 let xd = self.nodes.box_data(sx).map_or(0, |b| b.depth);
2237 if let Some(b) = self.nodes.box_data_mut(sx) {
2238 b.width += script_space;
2239 }
2240 let clr = self.math_par(mp::SUP_SHIFT_UP);
2241 if shift_up < clr {
2242 shift_up = clr;
2243 }
2244 let clr = xd + self.math_par(mp::SUP_BOTTOM_MIN);
2245 if shift_up < clr {
2246 shift_up = clr;
2247 }
2248 if subk.is_null() {
2249 if let Some(b) = self.nodes.box_data_mut(sx) {
2250 b.shift = -shift_up;
2251 }
2252 x = sx;
2253 } else {
2254 let st = if opts & noad_opt::NO_SUB_SCRIPT == noad_opt::NO_SUB_SCRIPT {
2255 cs
2256 } else {
2257 sub_style(cs)
2258 };
2259 let y = self.clean_box(subk, st, list_sub::SUB);
2260 self.nodes.flush_list(subk);
2261 let yh = self.nodes.box_data(y).map_or(0, |b| b.height);
2262 if let Some(b) = self.nodes.box_data_mut(y) {
2263 b.width += script_space;
2264 }
2265 let s2 = self.math_par(mp::SUB_SUP_SHIFT_DOWN);
2266 if shift_down < s2 {
2267 shift_down = s2;
2268 }
2269 let mut clr =
2270 self.math_par(mp::SUB_SUP_VGAP) - ((shift_up - xd) - (yh - shift_down));
2271 if clr > 0 {
2272 shift_down += clr;
2273 clr = self.math_par(mp::SUP_SUB_BOTTOM_MAX) - (shift_up - xd);
2274 if clr > 0 {
2275 shift_up += clr;
2276 shift_down -= clr;
2277 }
2278 }
2279 if let Some(b) = self.nodes.box_data_mut(sx) {
2280 b.shift = delta;
2281 }
2282 let p = self.new_kern_node((shift_up - xd) - (yh - shift_down), kern_sub::FONT);
2283 self.nodes.link(sx, p);
2284 self.nodes.link(p, y);
2285 x = self.vpack(sx, PackSpec::NATURAL);
2286 self.nodes.set_subtype(x, list_sub::SCRIPTS);
2287 if let Some(b) = self.nodes.box_data_mut(x) {
2288 b.shift = shift_down;
2289 }
2290 }
2291 }
2292 let nh = self.get_new_hlist(q);
2293 if nh.is_null() {
2294 self.set_new_hlist(q, x);
2295 } else {
2296 let t = self.nodes.tail_of(nh);
2297 self.nodes.link(t, x);
2298 }
2299 }
2300
2301 fn extensible_fence(
2307 &mut self,
2308 delim: NodeRef,
2309 height: i32,
2310 depth: i32,
2311 options: u32,
2312 axis: i32,
2313 ) -> NodeRef {
2314 let size = self.ts.math.size;
2315 if height == 0 && depth == 0 {
2316 let b = self.var_delimiter(delim, size, 0, false);
2317 if let Some(bd) = self.nodes.box_data_mut(b) {
2318 bd.shift = 0;
2319 }
2320 return b;
2321 }
2322 let up = if options & noad_opt::AXIS == noad_opt::AXIS {
2323 axis
2324 } else {
2325 0
2326 };
2327 let b = self.var_delimiter(delim, size, height + depth, false);
2328 if let Some(bd) = self.nodes.box_data_mut(b) {
2329 bd.shift = half(bd.height - bd.depth) - up;
2330 }
2331 let w = self.hpack_quiet(b);
2332 if options & noad_opt::EXACT != noad_opt::EXACT
2333 && let Some(bd) = self.nodes.box_data_mut(w)
2334 {
2335 bd.height = height + up;
2336 bd.depth = depth - up;
2337 }
2338 w
2339 }
2340
2341 fn make_left_right(&mut self, q: NodeRef, style: i32, max_d: i32, max_h: i32) -> NoadType {
2343 self.set_cur_style(style);
2344 let (delim, height, depth, options) = match self.nodes.data(q) {
2345 NodeData::Fence(f) => (f.delim, f.height, f.depth, f.options),
2346 _ => (NodeRef::NULL, 0, 0, 0),
2347 };
2348 let axis = self.math_par(mp::AXIS);
2349 if self.nodes.subtype(q) == fence_sub::NO {
2350 let b = self.extensible_fence(delim, height, depth, options, axis);
2351 self.set_new_hlist(q, b);
2352 return NoadType::Close;
2353 }
2354 let b = if height != 0 || depth != 0 {
2355 let total = height + depth;
2356 let b = self.var_delimiter(delim, self.ts.math.size, total, true);
2357 if options & noad_opt::NO_AXIS == noad_opt::NO_AXIS
2358 && let Some(bd) = self.nodes.box_data_mut(b)
2359 {
2360 bd.shift = 0;
2361 }
2362 b
2363 } else {
2364 let mut delta2 = max_d + axis;
2365 let mut delta1 = max_h + max_d - delta2;
2366 if delta2 > delta1 {
2367 delta1 = delta2;
2368 }
2369 let mut delta = (delta1 / 500) * self.eqtb.int(ip::DELIMITERFACTOR);
2370 delta2 = delta1 + delta1 - self.eqtb.dimen(dp::DELIMITERSHORTFALL);
2371 if delta < delta2 {
2372 delta = delta2;
2373 }
2374 self.var_delimiter(delim, self.ts.math.size, delta, false)
2375 };
2376 self.set_new_hlist(q, b);
2377 if self.nodes.subtype(q) == fence_sub::LEFT {
2378 NoadType::Open
2379 } else {
2380 NoadType::Close
2381 }
2382 }
2383
2384 fn second_pass(
2387 &mut self,
2388 mlist: NodeRef,
2389 style: i32,
2390 penalties: bool,
2391 max_h: i32,
2392 max_d: i32,
2393 ) -> NodeRef {
2394 let head = self.nodes.new_temp();
2395 let mut p = head;
2396 let mut q = mlist;
2397 let mut r_type: Option<NoadType> = None;
2398 self.set_cur_style(style);
2399 let binpen = self.eqtb.int(ip::BINOPPENALTY);
2400 let relpen = self.eqtb.int(ip::RELPENALTY);
2401 while !q.is_null() {
2402 let next = self.nodes.next(q);
2403 let Some(nt) = self.noad_type(q) else {
2404 if let NodeData::Style { style } = self.nodes.data(q) {
2405 let s = i32::from(*style);
2406 self.set_cur_style(s);
2407 self.nodes.free(q);
2408 } else {
2409 self.nodes.link(p, q);
2410 self.nodes.set_next(q, NodeRef::NULL);
2411 p = q;
2412 }
2413 q = next;
2414 continue;
2415 };
2416 let mut t = NoadType::Ord;
2417 let mut pen = 10000;
2418 match nt {
2419 NoadType::Op
2420 | NoadType::Open
2421 | NoadType::Close
2422 | NoadType::Punct
2423 | NoadType::Inner => t = nt,
2424 NoadType::Bin => {
2425 t = NoadType::Bin;
2426 pen = binpen;
2427 }
2428 NoadType::Rel => {
2429 t = NoadType::Rel;
2430 pen = relpen;
2431 }
2432 NoadType::Fraction => t = NoadType::Inner,
2433 NoadType::Left | NoadType::Right => {
2434 let saved = self.attr_from(q);
2435 t = self.make_left_right(q, style, max_d, max_h);
2436 self.attr_restore(saved);
2437 }
2438 _ => {}
2439 }
2440 if let Some(rt) = r_type {
2442 let style = self.ts.math.style as u32;
2443 if let Some((g, sub)) = self.math_spacing(rt as u32, t as u32, style) {
2444 let y = self.math_glue(g, self.ts.math.mu);
2445 let saved = self.attr_from(p);
2448 let z = self.new_glue_node(y, sub);
2449 self.attr_restore(saved);
2450 self.nodes.link(p, z);
2451 p = z;
2452 }
2453 }
2454 let nh = self.get_new_hlist(q);
2456 self.set_new_hlist(q, NodeRef::NULL);
2457 if !nh.is_null() {
2458 self.nodes.link(p, nh);
2459 p = self.nodes.tail_of(nh);
2460 }
2461 if penalties && !next.is_null() && pen < 10000 {
2462 let nid = self.nodes.id(next);
2463 let next_is_rel = self.noad_type(next) == Some(NoadType::Rel);
2464 if nid != id::PENALTY && !next_is_rel {
2465 let saved = self.attr_from(q);
2467 let z = self.new_penalty_node(pen);
2468 self.attr_restore(saved);
2469 self.nodes.set_subtype(z, 5);
2470 self.nodes.link(p, z);
2471 p = z;
2472 }
2473 }
2474 if nt == NoadType::Right {
2475 t = NoadType::Open;
2476 }
2477 r_type = Some(t);
2478 self.nodes.set_next(q, NodeRef::NULL);
2480 self.nodes.flush_list(q);
2481 q = next;
2482 }
2483 let first = self.nodes.next(head);
2484 self.nodes.free(head);
2485 if !first.is_null() {
2486 self.nodes.set_prev(first, NodeRef::NULL);
2487 }
2488 first
2489 }
2490}
2491
2492#[derive(Clone, Copy, PartialEq, Eq)]
2493enum Action {
2494 Convert,
2495 CheckDimensions,
2496 DoneWithNoad,
2497}