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 accent_char_box(&mut self, f: u32, c: u32, w: i32, fixed: bool) -> (NodeRef, bool) {
1361 if !fixed
1362 && self.is_opentype_math_font(f)
1363 && self.char_tag(f, c).0 != 2
1364 && self.fonts.char_metrics(f, c).is_some_and(|m| m.width < w)
1365 && let Some(parts) = self.math_char(f, c).and_then(|mc| mc.horiz.clone())
1366 {
1367 let overlap = self.math_par(mp::CONNECTOR_OVERLAP_MIN);
1368 return (self.ot_h_extensible_box(f, &parts, w, overlap), true);
1369 }
1370 (self.char_box(f, c), false)
1371 }
1372
1373 fn ot_h_extensible_box(
1376 &mut self,
1377 f: u32,
1378 parts: &[crate::fonts::ExtPart],
1379 w: i32,
1380 overlap: i32,
1381 ) -> NodeRef {
1382 let expand = |n: usize| -> Vec<crate::fonts::ExtPart> {
1383 let mut out = Vec::new();
1384 for p in parts.iter() {
1385 let k = if p.extender { n } else { 1 };
1386 for _ in 0..k {
1387 out.push(*p);
1388 }
1389 }
1390 out
1391 };
1392 let joint = |a: &crate::fonts::ExtPart, b: &crate::fonts::ExtPart| {
1393 let max = a.end.min(b.start).max(0);
1394 (max, overlap.min(max))
1395 };
1396 let largest = |seq: &[crate::fonts::ExtPart]| -> i64 {
1397 let mut t: i64 = seq.iter().map(|p| i64::from(p.advance)).sum();
1398 for win in seq.windows(2) {
1399 t -= i64::from(joint(&win[0], &win[1]).1);
1400 }
1401 t
1402 };
1403 let has_extender = parts.iter().any(|p| p.extender);
1404 let mut n = 0;
1405 let mut seq = expand(n);
1406 while has_extender && largest(&seq) < i64::from(w) && n < 1000 {
1407 n += 1;
1408 seq = expand(n);
1409 }
1410 let mut head = NodeRef::NULL;
1411 let mut tail = NodeRef::NULL;
1412 for (i, p) in seq.iter().enumerate() {
1413 if i > 0 {
1414 let (max, min) = joint(&seq[i - 1], p);
1415 let g = crate::glue::Glue {
1416 width: -max,
1417 stretch: max - min,
1418 ..crate::glue::Glue::ZERO
1419 };
1420 let gn = self.new_glue_node(g, 0);
1421 self.nodes.link(tail, gn);
1422 tail = gn;
1423 }
1424 let b = self.char_box(f, p.code);
1425 if head.is_null() {
1426 head = b;
1427 } else {
1428 self.nodes.link(tail, b);
1429 }
1430 tail = b;
1431 }
1432 let natural = largest(&seq) as i32;
1433 let target = w.max(natural.min(w));
1434 let b = self.hpack(head, PackSpec::Exactly(target));
1435 self.nodes.set_subtype(b, list_sub::H_EXTENSIBLE);
1436 b
1437 }
1438
1439 fn make_math_accent(&mut self, q: NodeRef) {
1441 let acc = self.nodes.noad(q).map_or(NodeRef::NULL, |n| n.accent);
1442 if acc.is_null() {
1443 self.make_bottom_accent(q);
1445 return;
1446 }
1447 let Some((f, mut c, mut i)) = self.fetch(acc) else {
1448 return;
1449 };
1450 let mut s = 0;
1452 let nuc = self.noad_field(q, Field::Nucleus);
1453 let mut nuc_char = None;
1454 if !nuc.is_null()
1455 && self.nodes.id(nuc) == id::MATH_CHAR
1456 && let Some((nf, nc, _)) = self.fetch(nuc)
1457 {
1458 nuc_char = Some((nf, nc));
1459 let sk = self.font(nf).skew_char;
1460 if sk >= 0
1461 && let Some(LigOp::Kern(k)) = self.lig_kern_step(nf, nc, sk as u32)
1462 {
1463 s = k;
1464 }
1465 }
1466 let st = cramped_style(self.ts.math.style);
1467 let mut char_nucleus = !nuc.is_null() && self.nodes.id(nuc) == id::MATH_CHAR;
1468 if nuc_char.is_none()
1469 && self.is_opentype_math_font(f)
1470 && let Some(fc) = self.nested_accent_char(nuc)
1471 {
1472 nuc_char = Some(fc);
1475 char_nucleus = true;
1476 }
1477 let mut x = self.clean_box(nuc, st, list_sub::NUCLEUS);
1478 let (w, mut h) = self
1479 .nodes
1480 .box_data(x)
1481 .map_or((0, 0), |b| (b.width, b.height));
1482 let fixed = matches!(
1484 self.nodes.subtype(q),
1485 crate::nodes::accent_sub::FIXED_TOP | crate::nodes::accent_sub::FIXED_BOTH
1486 );
1487 if !fixed {
1488 loop {
1489 let (tag, rem) = self.char_tag(f, c);
1490 if tag != 2 {
1491 break;
1492 }
1493 let Some(m) = self.fonts.char_metrics(f, rem).copied() else {
1494 break;
1495 };
1496 if m.width > w {
1497 break;
1498 }
1499 c = rem;
1500 i = m;
1501 }
1502 }
1503 let _ = i;
1504 let opentype = self.is_opentype_math_font(f);
1505 let xh = if opentype {
1507 self.font(f)
1508 .math_constants
1509 .as_ref()
1510 .and_then(|m| m.get("AccentBaseHeight").copied())
1511 .unwrap_or_else(|| self.x_height(f))
1512 } else {
1513 self.x_height(f)
1514 };
1515 let mut delta = if h < xh { h } else { xh };
1516 let sup = self.noad_field(q, Field::Sup);
1517 let sub = self.noad_field(q, Field::Sub);
1518 if (!sup.is_null() || !sub.is_null())
1519 && !nuc.is_null()
1520 && self.nodes.id(nuc) == id::MATH_CHAR
1521 {
1522 self.nodes.flush_list(x);
1524 let n = self.new_noad(noad_sub::ORD);
1525 let nk = self.take_field(q, Field::Nucleus);
1526 let a = self.take_field(q, Field::Sup);
1527 let b = self.take_field(q, Field::Sub);
1528 self.set_noad_field(n, Field::Nucleus, nk);
1529 self.set_noad_field(n, Field::Sup, a);
1530 self.set_noad_field(n, Field::Sub, b);
1531 let k = self.new_sub_list(true, n);
1532 self.set_noad_field(q, Field::Nucleus, k);
1533 x = self.clean_box(k, self.ts.math.style, list_sub::NUCLEUS);
1534 let nh = self.nodes.box_data(x).map_or(0, |b| b.height);
1535 delta = delta + nh - h;
1536 h = nh;
1537 }
1538 let saved = self.attr_from(acc);
1540 let (y, assembled) = self.accent_char_box(f, c, w, fixed);
1541 self.attr_restore(saved);
1542 let yw = self.nodes.box_data(y).map_or(0, |b| b.width);
1543 let shift = if opentype {
1544 let a = if char_nucleus {
1548 nuc_char
1549 .and_then(|(nf, nc)| self.math_char(nf, nc).and_then(|m| m.top_accent))
1550 .unwrap_or(half(w))
1551 } else {
1552 half(w)
1553 };
1554 let b = self
1555 .math_char(f, c)
1556 .and_then(|m| m.top_accent)
1557 .filter(|_| !assembled)
1558 .unwrap_or(half(yw));
1559 a - b
1560 } else if char_nucleus {
1561 s + half(w - yw)
1564 } else {
1565 s + half(w - yw) + 1
1566 };
1567 if let Some(b) = self.nodes.box_data_mut(y) {
1568 b.shift = shift;
1569 b.width = 0;
1570 }
1571 let p = self.new_kern_node(-delta, kern_sub::FONT);
1572 self.nodes.link(p, x);
1573 self.nodes.link(y, p);
1574 let y = self.vpack(y, PackSpec::NATURAL);
1575 self.nodes.set_subtype(y, list_sub::ACCENT);
1576 let mut xw = self.nodes.box_data(x).map_or(0, |b| b.width);
1577 if opentype
1581 && !self.math_124
1582 && !self.nodes.next(q).is_null()
1583 && let Some((nf, nc)) = nuc_char
1584 && self.nodes.id(nuc) == id::MATH_CHAR
1585 {
1586 xw += self.fonts.char_metrics(nf, nc).map_or(0, |m| m.italic);
1587 }
1588 let yh = self.nodes.box_data(y).map_or(0, |b| b.height);
1589 if let Some(b) = self.nodes.box_data_mut(y) {
1590 b.width = xw;
1591 }
1592 if yh < h {
1593 let p = self.new_kern_node(h - yh, kern_sub::FONT);
1594 let l = self.nodes.list_ptr(y);
1595 self.nodes.link(p, l);
1596 self.nodes.set_list_ptr(y, p);
1597 if let Some(b) = self.nodes.box_data_mut(y) {
1598 b.height = h;
1599 }
1600 }
1601 self.set_nucleus_box(q, y);
1602 let bot = self.nodes.noad(q).map_or(NodeRef::NULL, |n| n.bot_accent);
1603 if !bot.is_null() {
1604 self.make_bottom_accent(q);
1605 }
1606 }
1607
1608 fn make_bottom_accent(&mut self, q: NodeRef) {
1611 let bot = match self.nodes.noad_mut(q) {
1612 Some(n) => std::mem::replace(&mut n.bot_accent, NodeRef::NULL),
1613 None => return,
1614 };
1615 let Some((f, mut c, _)) = self.fetch(bot) else {
1616 self.nodes.flush_list(bot);
1617 return;
1618 };
1619 let bot_attr = self.attr_of(bot);
1620 self.nodes.flush_list(bot);
1621 let fixed = matches!(
1622 self.nodes.subtype(q),
1623 crate::nodes::accent_sub::FIXED_BOTTOM | crate::nodes::accent_sub::FIXED_BOTH
1624 );
1625 let nuc = self.noad_field(q, Field::Nucleus);
1626 let char_nucleus = !nuc.is_null() && self.nodes.id(nuc) == id::MATH_CHAR;
1627 let nuc_char = if char_nucleus {
1628 self.fetch(nuc).map(|(nf, nc, _)| (nf, nc))
1629 } else {
1630 None
1631 };
1632 let sup = self.noad_field(q, Field::Sup);
1634 let sub = self.noad_field(q, Field::Sub);
1635 let x = if char_nucleus && (!sup.is_null() || !sub.is_null()) {
1636 let n = self.new_noad(noad_sub::ORD);
1637 let nk = self.take_field(q, Field::Nucleus);
1638 let a = self.take_field(q, Field::Sup);
1639 let b = self.take_field(q, Field::Sub);
1640 self.set_noad_field(n, Field::Nucleus, nk);
1641 self.set_noad_field(n, Field::Sup, a);
1642 self.set_noad_field(n, Field::Sub, b);
1643 let k = self.new_sub_list(true, n);
1644 self.set_noad_field(q, Field::Nucleus, k);
1645 self.clean_box(k, self.ts.math.style, list_sub::NUCLEUS)
1646 } else {
1647 self.clean_box(nuc, self.ts.math.style, list_sub::NUCLEUS)
1648 };
1649 let (bw, xh) = self
1650 .nodes
1651 .box_data(x)
1652 .map_or((0, 0), |b| (b.width, b.height));
1653 let w = match nuc_char {
1656 Some((nf, nc)) => self.fonts.char_metrics(nf, nc).map_or(bw, |m| {
1657 if self.is_opentype_math_font(nf) {
1658 m.width
1659 } else {
1660 m.width + m.italic
1661 }
1662 }),
1663 None => bw,
1664 };
1665 if !fixed {
1667 loop {
1668 let (tag, rem) = self.char_tag(f, c);
1669 if tag != 2 {
1670 break;
1671 }
1672 match self.fonts.char_metrics(f, rem) {
1673 Some(m) if m.width <= w => c = rem,
1674 _ => break,
1675 }
1676 }
1677 }
1678 let saved = self.attr_set(bot_attr);
1679 let (y, assembled) = self.accent_char_box(f, c, w, fixed);
1680 self.attr_restore(saved);
1681 let yw = self.nodes.box_data(y).map_or(0, |b| b.width);
1682 let shift = if self.is_opentype_math_font(f) {
1683 let a = nuc_char
1684 .filter(|_| self.noad_field(q, Field::Sup).is_null())
1685 .and_then(|(nf, nc)| self.math_char(nf, nc).and_then(|m| m.top_accent))
1686 .unwrap_or(half(w));
1687 let b = self
1688 .math_char(f, c)
1689 .and_then(|m| m.top_accent)
1690 .filter(|_| !assembled)
1691 .unwrap_or(half(yw));
1692 a - b
1693 } else if nuc_char.is_some() {
1694 half(w - yw)
1695 } else {
1696 w / 2 - yw / 2 + 1
1697 };
1698 if let Some(b) = self.nodes.box_data_mut(y) {
1699 b.shift = shift;
1700 b.width = 0;
1701 }
1702 self.nodes.link(x, y);
1703 let v = self.vpack(x, PackSpec::NATURAL);
1706 self.nodes.set_subtype(v, list_sub::ACCENT);
1707 if let Some(b) = self.nodes.box_data_mut(v) {
1708 b.shift = b.height - xh;
1709 b.width = bw;
1710 }
1711 self.set_nucleus_box(q, v);
1712 }
1713
1714 fn make_fraction(&mut self, q: NodeRef) {
1716 let (mut thickness, num, denom, left, right, options) = match self.nodes.data(q) {
1717 NodeData::Fraction(f) => (f.thickness, f.num, f.denom, f.left, f.right, f.options),
1718 _ => return,
1719 };
1720 if thickness == DEFAULT_CODE {
1721 thickness = self.math_par(mp::FRACTION_RULE);
1722 }
1723 let cs = self.ts.math.style;
1724 let mut x = self.clean_box(num, num_style(cs), list_sub::NUMERATOR);
1725 let mut z = self.clean_box(denom, denom_style(cs), list_sub::DENOMINATOR);
1726 let (xw, zw) = (
1727 self.nodes.box_data(x).map_or(0, |b| b.width),
1728 self.nodes.box_data(z).map_or(0, |b| b.width),
1729 );
1730 if xw < zw {
1731 x = self.rebox(x, zw);
1732 } else {
1733 z = self.rebox(z, xw);
1734 }
1735 let (mut shift_up, mut shift_down) = if thickness == 0 {
1736 (
1737 self.math_par(mp::STACK_NUM_UP),
1738 self.math_par(mp::STACK_DENOM_DOWN),
1739 )
1740 } else {
1741 (
1742 self.math_par(mp::FRACTION_NUM_UP),
1743 self.math_par(mp::FRACTION_DENOM_DOWN),
1744 )
1745 };
1746 let (xh, xd) = self
1747 .nodes
1748 .box_data(x)
1749 .map_or((0, 0), |b| (b.height, b.depth));
1750 let (zh, zd) = self
1751 .nodes
1752 .box_data(z)
1753 .map_or((0, 0), |b| (b.height, b.depth));
1754 let axis = self.math_par(mp::AXIS);
1755 let delta;
1756 if thickness == 0 {
1757 let clr = self.math_par(mp::STACK_VGAP);
1758 delta = half(clr - ((shift_up - xd) - (zh - shift_down)));
1759 if delta > 0 {
1760 shift_up += delta;
1761 shift_down += delta;
1762 }
1763 } else {
1764 let exact = options & noad_opt::EXACT == noad_opt::EXACT;
1765 let (clr_n, clr_d) = if exact {
1766 (0, 0)
1767 } else if thickness == self.math_par(mp::FRACTION_RULE) {
1768 (
1769 self.math_par(mp::FRACTION_NUM_VGAP),
1770 self.math_par(mp::FRACTION_DENOM_VGAP),
1771 )
1772 } else if cs < 2 {
1773 (3 * thickness, 3 * thickness)
1774 } else {
1775 (thickness, thickness)
1776 };
1777 delta = half(thickness);
1778 let delta1 = clr_n - ((shift_up - xd) - (axis + delta));
1779 let delta2 = clr_d - ((axis - delta) - (zh - shift_down));
1780 if delta1 > 0 {
1781 shift_up += delta1;
1782 }
1783 if delta2 > 0 {
1784 shift_down += delta2;
1785 }
1786 }
1787 let v = self.new_null_box_node(false);
1789 self.nodes.set_subtype(v, list_sub::FRACTION);
1790 let width = self.nodes.box_data(x).map_or(0, |b| b.width);
1791 if let Some(b) = self.nodes.box_data_mut(v) {
1792 b.height = shift_up + xh;
1793 b.depth = zd + shift_down;
1794 b.width = width;
1795 }
1796 let p;
1797 if thickness == 0 {
1798 p = self.new_kern_node((shift_up - xd) - (zh - shift_down), kern_sub::FONT);
1799 self.nodes.link(p, z);
1800 } else {
1801 let y = self.fraction_rule(thickness);
1802 let p2 = self.new_kern_node((axis - delta) - (zh - shift_down), kern_sub::FONT);
1803 self.nodes.link(y, p2);
1804 self.nodes.link(p2, z);
1805 p = self.new_kern_node((shift_up - xd) - (axis + delta), kern_sub::FONT);
1806 self.nodes.link(p, y);
1807 }
1808 self.nodes.link(x, p);
1809 self.nodes.set_list_ptr(v, x);
1810 let dsize = self.math_par(mp::FRACTION_DEL_SIZE);
1812 let size = self.ts.math.size;
1813 let lx = self.var_delimiter(left, size, dsize, left.is_null());
1817 self.nodes.link(lx, v);
1818 let rz = self.var_delimiter(right, size, dsize, right.is_null());
1819 self.nodes.link(v, rz);
1820 let h = self.hpack_quiet(lx);
1821 self.nodes.set_subtype(h, list_sub::FRACTION);
1822 self.set_new_hlist(q, h);
1823 }
1824
1825 fn make_op(&mut self, q: NodeRef) -> i32 {
1827 let cs = self.ts.math.style;
1828 if self.nodes.subtype(q) == noad_sub::OP_DISPLAY_LIMITS && cs < 2 {
1829 self.nodes.set_subtype(q, noad_sub::OP_LIMITS);
1830 }
1831 let limits = self.nodes.subtype(q) == noad_sub::OP_LIMITS;
1832 let nuc = self.noad_field(q, Field::Nucleus);
1833 let mut delta = 0;
1834 let mut opentype = false;
1835 if !nuc.is_null() && self.nodes.id(nuc) == id::MATH_CHAR {
1836 if let Some((f, c, m)) = self.fetch(nuc) {
1837 let mut m = m;
1838 if cs < 2 && self.is_opentype_math_font(f) {
1839 let target = self.math_par(mp::OPERATOR_SIZE);
1843 let mut c = c;
1844 let mut guard = 0;
1845 while m.height + m.depth < target && guard < 64 {
1846 guard += 1;
1847 let (tag, rem) = self.char_tag(f, c);
1848 let Some(m2) = self
1849 .fonts
1850 .char_metrics(f, rem)
1851 .copied()
1852 .filter(|_| tag == 2)
1853 else {
1854 break;
1855 };
1856 m = m2;
1857 c = rem;
1858 }
1859 if let NodeData::MathChar { chr, .. } = self.nodes.data_mut(nuc) {
1860 *chr = c;
1861 }
1862 } else if cs < 2 {
1863 let (tag, rem) = self.char_tag(f, c);
1864 if tag == 2
1865 && let Some(m2) = self.fonts.char_metrics(f, rem).copied()
1866 {
1867 m = m2;
1868 if let NodeData::MathChar { chr, .. } = self.nodes.data_mut(nuc) {
1869 *chr = rem;
1870 }
1871 }
1872 }
1873 delta = m.italic;
1874 opentype = self.is_opentype_math_font(f);
1875 }
1876 let saved = std::mem::replace(&mut self.math_op_nucleus, true);
1877 let x = self.clean_box(nuc, cs, list_sub::NUCLEUS);
1878 self.math_op_nucleus = saved;
1879 let has_sub = !self.noad_field(q, Field::Sub).is_null();
1880 let no_limits = self.nodes.subtype(q) == noad_sub::OP_NO_LIMITS;
1881 let axis = self.math_par(mp::AXIS);
1882 let math_124 = self.math_124;
1883 if let Some(b) = self.nodes.box_data_mut(x) {
1884 if has_sub && !limits && !opentype {
1885 b.width -= delta;
1886 }
1887 if opentype && math_124 {
1888 b.width += delta;
1892 if no_limits {
1893 b.width -= delta;
1894 }
1895 if has_sub && !limits {
1896 b.width -= delta;
1897 }
1898 }
1899 b.shift = half(b.height - b.depth) - axis;
1900 }
1901 self.set_nucleus_box(q, x);
1902 if opentype && !limits {
1907 if !has_sub {
1908 delta = 0;
1909 } else if !math_124 {
1910 self.ts.math.op_kern = -delta;
1911 }
1912 }
1913 }
1914 if limits {
1915 let supk = self.noad_field(q, Field::Sup);
1917 let subk = self.noad_field(q, Field::Sub);
1918 let x = self.clean_box(supk, sup_style(cs), list_sub::SUP);
1919 let nk = self.noad_field(q, Field::Nucleus);
1920 let y = self.clean_box(nk, cs, list_sub::NUCLEUS);
1921 let z = self.clean_box(subk, sub_style(cs), list_sub::SUB);
1922 let v = self.new_null_box_node(false);
1923 self.nodes.set_subtype(v, list_sub::LIMITS);
1924 let wy = self.nodes.box_data(y).map_or(0, |b| b.width);
1925 let wx = self.nodes.box_data(x).map_or(0, |b| b.width);
1926 let wz = self.nodes.box_data(z).map_or(0, |b| b.width);
1927 let wv = wy.max(wx).max(wz);
1928 let x = self.rebox(x, wv);
1929 let y = self.rebox(y, wv);
1930 let z = self.rebox(z, wv);
1931 if let Some(b) = self.nodes.box_data_mut(x) {
1932 b.shift = half(delta);
1933 }
1934 if let Some(b) = self.nodes.box_data_mut(z) {
1935 b.shift = -half(delta);
1936 }
1937 let (yh, yd) = self
1938 .nodes
1939 .box_data(y)
1940 .map_or((0, 0), |b| (b.height, b.depth));
1941 let (mut vh, mut vd) = (yh, yd);
1942 let kern = self.math_par(mp::LIMIT_ABOVE_KERN);
1943 let kern_below = self.math_par(mp::LIMIT_BELOW_KERN);
1944 let head;
1945 if supk.is_null() {
1946 self.nodes.free(x);
1947 head = y;
1948 } else {
1949 let (xh, xd) = self
1950 .nodes
1951 .box_data(x)
1952 .map_or((0, 0), |b| (b.height, b.depth));
1953 let mut shift_up = self.math_par(mp::LIMIT_ABOVE_BGAP) - xd;
1954 let g1 = self.math_par(mp::LIMIT_ABOVE_VGAP);
1955 if shift_up < g1 {
1956 shift_up = g1;
1957 }
1958 let p = self.new_kern_node(shift_up, kern_sub::FONT);
1959 self.nodes.link(p, y);
1960 self.nodes.link(x, p);
1961 let p = self.new_kern_node(kern, kern_sub::FONT);
1962 self.nodes.link(p, x);
1963 head = p;
1964 vh += kern + xh + xd + shift_up;
1965 }
1966 if subk.is_null() {
1967 self.nodes.free(z);
1968 } else {
1969 let (zh, zd) = self
1970 .nodes
1971 .box_data(z)
1972 .map_or((0, 0), |b| (b.height, b.depth));
1973 let mut shift_down = self.math_par(mp::LIMIT_BELOW_BGAP) - zh;
1974 let g2 = self.math_par(mp::LIMIT_BELOW_VGAP);
1975 if shift_down < g2 {
1976 shift_down = g2;
1977 }
1978 let p = self.new_kern_node(shift_down, kern_sub::FONT);
1979 self.nodes.link(y, p);
1980 self.nodes.link(p, z);
1981 let p = self.new_kern_node(kern_below, kern_sub::FONT);
1982 self.nodes.link(z, p);
1983 vd += kern_below + zh + zd + shift_down;
1984 }
1985 if let Some(b) = self.nodes.box_data_mut(v) {
1986 b.width = wv;
1987 b.height = vh;
1988 b.depth = vd;
1989 b.head = head;
1990 }
1991 let a = self.take_field(q, Field::Sup);
1993 let b = self.take_field(q, Field::Sub);
1994 self.nodes.flush_list(a);
1995 self.nodes.flush_list(b);
1996 self.set_new_hlist(q, v);
1997 }
1998 delta
1999 }
2000
2001 fn make_ord(&mut self, q: NodeRef) {
2003 'restart: loop {
2004 if !self.noad_field(q, Field::Sub).is_null()
2005 || !self.noad_field(q, Field::Sup).is_null()
2006 {
2007 return;
2008 }
2009 let nuc = self.noad_field(q, Field::Nucleus);
2010 if nuc.is_null() || self.nodes.id(nuc) != id::MATH_CHAR {
2011 return;
2012 }
2013 let p = self.nodes.next(q);
2014 if p.is_null()
2015 || self.nodes.id(p) != id::NOAD
2016 || !matches!(
2017 self.nodes.subtype(p),
2018 noad_sub::ORD
2019 | noad_sub::OP_DISPLAY_LIMITS
2020 | noad_sub::OP_LIMITS
2021 | noad_sub::OP_NO_LIMITS
2022 | noad_sub::BIN
2023 | noad_sub::REL
2024 | noad_sub::OPEN
2025 | noad_sub::CLOSE
2026 | noad_sub::PUNCT
2027 )
2028 {
2029 return;
2030 }
2031 let pn = self.noad_field(p, Field::Nucleus);
2032 if pn.is_null() || self.nodes.id(pn) != id::MATH_CHAR {
2033 return;
2034 }
2035 let (qfam, pfam, pchr) = match (self.nodes.data(nuc), self.nodes.data(pn)) {
2036 (NodeData::MathChar { fam: a, .. }, NodeData::MathChar { fam: b, chr }) => {
2037 (*a, *b, *chr)
2038 }
2039 _ => return,
2040 };
2041 if qfam != pfam {
2042 return;
2043 }
2044 self.nodes.get_mut(nuc).id = id::MATH_TEXT_CHAR;
2045 let Some((f, c, _)) = self.fetch(nuc) else {
2046 return;
2047 };
2048 let Some(op) = self
2049 .lig_kern_step(f, c, pchr)
2050 .or_else(|| self.base_mode_kern(f, c, pchr))
2051 else {
2052 return;
2053 };
2054 match op {
2055 LigOp::Kern(k) => {
2056 let kn = self.new_kern_node(k, kern_sub::FONT);
2057 let n = self.nodes.next(q);
2058 self.nodes.link(kn, n);
2059 self.nodes.link(q, kn);
2060 return;
2061 }
2062 LigOp::Lig { op, chr } => {
2063 match op {
2064 1 | 5 => self.set_char(nuc, chr),
2065 2 | 6 => self.set_char(pn, chr),
2066 3 | 7 | 11 => {
2067 let r = self.new_noad(noad_sub::ORD);
2068 let k = self.new_math_char(qfam, chr);
2069 if op >= 11 {
2070 self.nodes.get_mut(k).id = id::MATH_TEXT_CHAR;
2071 }
2072 self.set_noad_field(r, Field::Nucleus, k);
2073 self.nodes.link(q, r);
2074 self.nodes.link(r, p);
2075 }
2076 _ => {
2077 let n = self.nodes.next(p);
2078 self.nodes.link(q, n);
2079 if n.is_null() {
2080 self.nodes.set_next(q, NodeRef::NULL);
2081 }
2082 self.set_char(nuc, chr);
2083 let a = self.take_field(p, Field::Sub);
2084 let b = self.take_field(p, Field::Sup);
2085 self.set_noad_field(q, Field::Sub, a);
2086 self.set_noad_field(q, Field::Sup, b);
2087 self.nodes.set_next(p, NodeRef::NULL);
2088 self.nodes.flush_list(p);
2089 }
2090 }
2091 if op > 3 {
2092 return;
2093 }
2094 self.nodes.get_mut(nuc).id = id::MATH_CHAR;
2095 continue 'restart;
2096 }
2097 }
2098 }
2099 }
2100
2101 fn set_char(&mut self, k: NodeRef, c: u32) {
2102 if let NodeData::MathChar { chr, .. } = self.nodes.data_mut(k) {
2103 *chr = c;
2104 }
2105 }
2106
2107 fn convert_nucleus(&mut self, q: NodeRef, delta: i32) {
2110 let mut delta = delta;
2111 let nuc = self.noad_field(q, Field::Nucleus);
2112 let kid = if nuc.is_null() { 0 } else { self.nodes.id(nuc) };
2113 let has_sub = !self.noad_field(q, Field::Sub).is_null();
2114 let p = match kid {
2115 id::MATH_CHAR | id::MATH_TEXT_CHAR => match self.fetch(nuc) {
2116 Some((f, c, m)) => {
2117 delta = m.italic;
2118 let saved = self.attr_from(nuc);
2121 let p = self.new_math_glyph(f, c);
2122 let has_sup = !self.noad_field(q, Field::Sup).is_null();
2123 if self.is_opentype_math_font(f) {
2124 if kid == id::MATH_CHAR
2130 && !has_sup
2131 && !has_sub
2132 && (!self.math_124 || self.math_op_nucleus)
2133 {
2134 delta = 0;
2135 }
2136 } else if kid == id::MATH_TEXT_CHAR
2137 && self.font(f).params.get(2).copied().unwrap_or(0) != 0
2138 && !self.math_124
2139 {
2140 delta = 0;
2144 }
2145 if kid == id::MATH_TEXT_CHAR && self.is_opentype_math_font(f) {
2148 self.attr_restore(saved.clone());
2149 }
2150 if !has_sub && delta != 0 {
2151 let k = self.new_kern_node(delta, kern_sub::ITALIC);
2152 self.nodes.link(p, k);
2153 delta = 0;
2154 }
2155 self.attr_restore(saved);
2156 p
2157 }
2158 None => NodeRef::NULL,
2159 },
2160 id::SUB_BOX => {
2161 let b = self.nodes.list_ptr_kernel(nuc);
2162 self.nodes.set_kernel_list(nuc, NodeRef::NULL);
2163 let k = std::mem::take(&mut self.ts.math.op_kern);
2164 if k != 0 && !b.is_null() {
2165 let kn = self.new_kern_node(k, kern_sub::FONT);
2166 self.nodes.link(b, kn);
2167 }
2168 b
2169 }
2170 id::SUB_MLIST => {
2171 let l = self.nodes.list_ptr_kernel(nuc);
2172 self.nodes.set_kernel_list(nuc, NodeRef::NULL);
2173 let hl = self.clean_box_mlist(l, self.ts.math.style);
2174 let saved = self.attr_from(nuc);
2176 let b = self.hpack_quiet(hl);
2177 self.attr_restore(saved);
2178 b
2179 }
2180 _ => {
2181 if has_sub || !self.noad_field(q, Field::Sup).is_null() {
2183 self.new_null_box_node(true)
2184 } else {
2185 NodeRef::NULL
2186 }
2187 }
2188 };
2189 self.set_new_hlist(q, p);
2190 let has_sup = !self.noad_field(q, Field::Sup).is_null();
2191 if !has_sub && !has_sup {
2192 return;
2193 }
2194 self.make_scripts(q, delta);
2195 }
2196
2197 fn make_scripts(&mut self, q: NodeRef, delta: i32) {
2199 let p = self.get_new_hlist(q);
2200 let cs = self.ts.math.style;
2201 let (mut shift_up, mut shift_down);
2202 if !p.is_null() && self.nodes.is_glyph(p) {
2203 shift_up = 0;
2204 shift_down = 0;
2205 } else {
2206 let (h, d) = self.list_height_depth(p);
2207 shift_up = h - self.math_par(mp::SUP_SHIFT_DROP);
2208 shift_down = d + self.math_par(mp::SUB_SHIFT_DROP);
2209 }
2210 let opts = self.nodes.noad(q).map_or(0, |n| n.options);
2211 let supk = self.take_field(q, Field::Sup);
2212 let subk = self.take_field(q, Field::Sub);
2213 if !p.is_null() && self.nodes.is_glyph(p) {
2214 for k in [supk, subk] {
2217 if !k.is_null() && self.nodes.id(k) == id::MATH_CHAR {
2218 let _ = self.fetch(k);
2219 }
2220 }
2221 }
2222 let script_space = self.math_par(mp::SPACE_AFTER_SCRIPT);
2223 let x;
2224 if supk.is_null() {
2225 let st = if opts & noad_opt::NO_SUB_SCRIPT == noad_opt::NO_SUB_SCRIPT {
2227 cs
2228 } else {
2229 sub_style(cs)
2230 };
2231 x = self.clean_box(subk, st, list_sub::SUB);
2232 self.nodes.flush_list(subk);
2233 let xh = self.nodes.box_data(x).map_or(0, |b| b.height);
2234 if let Some(b) = self.nodes.box_data_mut(x) {
2235 b.width += script_space;
2236 }
2237 let s1 = self.math_par(mp::SUB_SHIFT_DOWN);
2238 if shift_down < s1 {
2239 shift_down = s1;
2240 }
2241 let clr = xh - self.math_par(mp::SUB_TOP_MAX);
2242 if shift_down < clr {
2243 shift_down = clr;
2244 }
2245 if let Some(b) = self.nodes.box_data_mut(x) {
2246 b.shift = shift_down;
2247 }
2248 } else {
2249 let st = if opts & noad_opt::NO_SUPER_SCRIPT == noad_opt::NO_SUPER_SCRIPT {
2250 cs
2251 } else {
2252 sup_style(cs)
2253 };
2254 let sx = self.clean_box(supk, st, list_sub::SUP);
2255 self.nodes.flush_list(supk);
2256 let xd = self.nodes.box_data(sx).map_or(0, |b| b.depth);
2257 if let Some(b) = self.nodes.box_data_mut(sx) {
2258 b.width += script_space;
2259 }
2260 let clr = self.math_par(mp::SUP_SHIFT_UP);
2261 if shift_up < clr {
2262 shift_up = clr;
2263 }
2264 let clr = xd + self.math_par(mp::SUP_BOTTOM_MIN);
2265 if shift_up < clr {
2266 shift_up = clr;
2267 }
2268 if subk.is_null() {
2269 if let Some(b) = self.nodes.box_data_mut(sx) {
2270 b.shift = -shift_up;
2271 }
2272 x = sx;
2273 } else {
2274 let st = if opts & noad_opt::NO_SUB_SCRIPT == noad_opt::NO_SUB_SCRIPT {
2275 cs
2276 } else {
2277 sub_style(cs)
2278 };
2279 let y = self.clean_box(subk, st, list_sub::SUB);
2280 self.nodes.flush_list(subk);
2281 let yh = self.nodes.box_data(y).map_or(0, |b| b.height);
2282 if let Some(b) = self.nodes.box_data_mut(y) {
2283 b.width += script_space;
2284 }
2285 let s2 = self.math_par(mp::SUB_SUP_SHIFT_DOWN);
2286 if shift_down < s2 {
2287 shift_down = s2;
2288 }
2289 let mut clr =
2290 self.math_par(mp::SUB_SUP_VGAP) - ((shift_up - xd) - (yh - shift_down));
2291 if clr > 0 {
2292 shift_down += clr;
2293 clr = self.math_par(mp::SUP_SUB_BOTTOM_MAX) - (shift_up - xd);
2294 if clr > 0 {
2295 shift_up += clr;
2296 shift_down -= clr;
2297 }
2298 }
2299 if let Some(b) = self.nodes.box_data_mut(sx) {
2300 b.shift = delta;
2301 }
2302 let p = self.new_kern_node((shift_up - xd) - (yh - shift_down), kern_sub::FONT);
2303 self.nodes.link(sx, p);
2304 self.nodes.link(p, y);
2305 x = self.vpack(sx, PackSpec::NATURAL);
2306 self.nodes.set_subtype(x, list_sub::SCRIPTS);
2307 if let Some(b) = self.nodes.box_data_mut(x) {
2308 b.shift = shift_down;
2309 }
2310 }
2311 }
2312 let nh = self.get_new_hlist(q);
2313 if nh.is_null() {
2314 self.set_new_hlist(q, x);
2315 } else {
2316 let t = self.nodes.tail_of(nh);
2317 self.nodes.link(t, x);
2318 }
2319 }
2320
2321 fn extensible_fence(
2327 &mut self,
2328 delim: NodeRef,
2329 height: i32,
2330 depth: i32,
2331 options: u32,
2332 axis: i32,
2333 ) -> NodeRef {
2334 let size = self.ts.math.size;
2335 if height == 0 && depth == 0 {
2336 let b = self.var_delimiter(delim, size, 0, false);
2337 if let Some(bd) = self.nodes.box_data_mut(b) {
2338 bd.shift = 0;
2339 }
2340 return b;
2341 }
2342 let up = if options & noad_opt::AXIS == noad_opt::AXIS {
2343 axis
2344 } else {
2345 0
2346 };
2347 let b = self.var_delimiter(delim, size, height + depth, false);
2348 if let Some(bd) = self.nodes.box_data_mut(b) {
2349 bd.shift = half(bd.height - bd.depth) - up;
2350 }
2351 let w = self.hpack_quiet(b);
2352 if options & noad_opt::EXACT != noad_opt::EXACT
2353 && let Some(bd) = self.nodes.box_data_mut(w)
2354 {
2355 bd.height = height + up;
2356 bd.depth = depth - up;
2357 }
2358 w
2359 }
2360
2361 fn make_left_right(&mut self, q: NodeRef, style: i32, max_d: i32, max_h: i32) -> NoadType {
2363 self.set_cur_style(style);
2364 let (delim, height, depth, options) = match self.nodes.data(q) {
2365 NodeData::Fence(f) => (f.delim, f.height, f.depth, f.options),
2366 _ => (NodeRef::NULL, 0, 0, 0),
2367 };
2368 let axis = self.math_par(mp::AXIS);
2369 if self.nodes.subtype(q) == fence_sub::NO {
2370 let b = self.extensible_fence(delim, height, depth, options, axis);
2371 self.set_new_hlist(q, b);
2372 return NoadType::Close;
2373 }
2374 let b = if height != 0 || depth != 0 {
2375 let total = height + depth;
2376 let b = self.var_delimiter(delim, self.ts.math.size, total, true);
2377 if options & noad_opt::NO_AXIS == noad_opt::NO_AXIS
2378 && let Some(bd) = self.nodes.box_data_mut(b)
2379 {
2380 bd.shift = 0;
2381 }
2382 b
2383 } else {
2384 let mut delta2 = max_d + axis;
2385 let mut delta1 = max_h + max_d - delta2;
2386 if delta2 > delta1 {
2387 delta1 = delta2;
2388 }
2389 let mut delta = (delta1 / 500) * self.eqtb.int(ip::DELIMITERFACTOR);
2390 delta2 = delta1 + delta1 - self.eqtb.dimen(dp::DELIMITERSHORTFALL);
2391 if delta < delta2 {
2392 delta = delta2;
2393 }
2394 self.var_delimiter(delim, self.ts.math.size, delta, false)
2395 };
2396 self.set_new_hlist(q, b);
2397 if self.nodes.subtype(q) == fence_sub::LEFT {
2398 NoadType::Open
2399 } else {
2400 NoadType::Close
2401 }
2402 }
2403
2404 fn second_pass(
2407 &mut self,
2408 mlist: NodeRef,
2409 style: i32,
2410 penalties: bool,
2411 max_h: i32,
2412 max_d: i32,
2413 ) -> NodeRef {
2414 let head = self.nodes.new_temp();
2415 let mut p = head;
2416 let mut q = mlist;
2417 let mut r_type: Option<NoadType> = None;
2418 self.set_cur_style(style);
2419 let binpen = self.eqtb.int(ip::BINOPPENALTY);
2420 let relpen = self.eqtb.int(ip::RELPENALTY);
2421 while !q.is_null() {
2422 let next = self.nodes.next(q);
2423 let Some(nt) = self.noad_type(q) else {
2424 if let NodeData::Style { style } = self.nodes.data(q) {
2425 let s = i32::from(*style);
2426 self.set_cur_style(s);
2427 self.nodes.free(q);
2428 } else {
2429 self.nodes.link(p, q);
2430 self.nodes.set_next(q, NodeRef::NULL);
2431 p = q;
2432 }
2433 q = next;
2434 continue;
2435 };
2436 let mut t = NoadType::Ord;
2437 let mut pen = 10000;
2438 match nt {
2439 NoadType::Op
2440 | NoadType::Open
2441 | NoadType::Close
2442 | NoadType::Punct
2443 | NoadType::Inner => t = nt,
2444 NoadType::Bin => {
2445 t = NoadType::Bin;
2446 pen = binpen;
2447 }
2448 NoadType::Rel => {
2449 t = NoadType::Rel;
2450 pen = relpen;
2451 }
2452 NoadType::Fraction => t = NoadType::Inner,
2453 NoadType::Left | NoadType::Right => {
2454 let saved = self.attr_from(q);
2455 t = self.make_left_right(q, style, max_d, max_h);
2456 self.attr_restore(saved);
2457 }
2458 _ => {}
2459 }
2460 if let Some(rt) = r_type {
2462 let style = self.ts.math.style as u32;
2463 if let Some((g, sub)) = self.math_spacing(rt as u32, t as u32, style) {
2464 let y = self.math_glue(g, self.ts.math.mu);
2465 let saved = self.attr_from(p);
2468 let z = self.new_glue_node(y, sub);
2469 self.attr_restore(saved);
2470 self.nodes.link(p, z);
2471 p = z;
2472 }
2473 }
2474 let nh = self.get_new_hlist(q);
2476 self.set_new_hlist(q, NodeRef::NULL);
2477 if !nh.is_null() {
2478 self.nodes.link(p, nh);
2479 p = self.nodes.tail_of(nh);
2480 }
2481 if penalties && !next.is_null() && pen < 10000 {
2482 let nid = self.nodes.id(next);
2483 let next_is_rel = self.noad_type(next) == Some(NoadType::Rel);
2484 if nid != id::PENALTY && !next_is_rel {
2485 let saved = self.attr_from(q);
2487 let z = self.new_penalty_node(pen);
2488 self.attr_restore(saved);
2489 self.nodes.set_subtype(z, 5);
2490 self.nodes.link(p, z);
2491 p = z;
2492 }
2493 }
2494 r_type = Some(t);
2498 self.nodes.set_next(q, NodeRef::NULL);
2500 self.nodes.flush_list(q);
2501 q = next;
2502 }
2503 let first = self.nodes.next(head);
2504 self.nodes.free(head);
2505 if !first.is_null() {
2506 self.nodes.set_prev(first, NodeRef::NULL);
2507 }
2508 first
2509 }
2510}
2511
2512#[derive(Clone, Copy, PartialEq, Eq)]
2513enum Action {
2514 Convert,
2515 CheckDimensions,
2516 DoneWithNoad,
2517}