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