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