Repository: typst/typst
File: crates/typst-layout/src/math/text.rs (Rust)

Code before:
                .into()
        }
    };
    ctx.push(fragment);
    Ok(())
}

/// Layout a [`GlyphFragment`].
fn layout_glyph(
    mut glyph: GlyphFragment,
    dtls: bool,
    ctx: &mut MathContext,
    styles: StyleChain,
) -> MathFragment {
    if dtls {
        glyph.make_dotless_form(ctx);
    }
    let math_size = EquationElem::size_in(styles);
    match math_size {
        MathSize::Script => glyph.make_script_size(ctx),
        MathSize::ScriptScript => glyph.make_script_script_size(ctx),
        _ => {}
    }

    if glyph.class == MathClass::Large {
        let mut variant = if math_size == MathSize::Display {
            let height = scaled!(ctx, styles, display_operator_min_height)
                .max(SQRT_2 * glyph.height());
            glyph.stretch_vertical(ctx, height, Abs::zero())
        } else {
            glyph.into_variant()
        };
        // TeXbook p 155. Large operators are always vertically centered on the
        // axis.
        variant.center_on_axis(ctx);
        variant.into()
    } else {
        glyph.into()
    }
}

/// Style a character.
fn styled_char(styles: StyleChain, c: char, auto_italic: bool) -> char {
    let variant = EquationElem::variant_in(styles);
    let bold = EquationElem::bold_in(styles);
    let italic =
        EquationElem::italic_in(styles).or_else(|| (!auto_italic).then_some(false));

    if let Some(c) = basic_exception(c) {
        return c;
    }

Review comment (suggestion):
I'd like to keep this part of the comment if that's ok. It's useful context that the codepoint itself is being changed, even if it's implied by the function signature. It was confusing to me when I first saw this.

Code after:
    let variant = styles.get(EquationElem::variant);
    let bold = styles.get(EquationElem::bold);
    let italic = styles.get(EquationElem::italic);

    let style = MathStyle::select(unstyled_c, variant, bold, italic);
    let text: EcoString = to_style(unstyled_c, style).collect();

    let fragment: MathFragment =
        match GlyphFragment::new(ctx.font, symbol_styles, &text, elem.span()) {
            Ok(mut glyph) => {
                adjust_glyph_layout(&mut glyph, ctx, styles);
                glyph.into()
            }
            Err(_) => {
                // Not in the math font, fallback to normal inline text layout.
                // TODO: Should replace this with proper fallback in [`GlyphFragment::new`].
                layout_inline_text(&text, elem.span(), ctx, styles)?.into()
            }
        };
    ctx.push(fragment);
    Ok(())
}

/// Centers large glyphs vertically on the axis, scaling them if in display
/// style.
fn adjust_glyph_layout(
    glyph: &mut GlyphFragment,
    ctx: &mut MathContext,
    styles: StyleChain,
) {
    if glyph.class == MathClass::Large {
        if styles.get(EquationElem::size) == MathSize::Display {
            let height = scaled!(ctx, styles, display_operator_min_height)
                .max(SQRT_2 * glyph.size.y);
            glyph.stretch_vertical(ctx, height);
        };
        // TeXbook p 155. Large operators are always vertically centered on the
        // axis.
        glyph.center_on_axis();
    }
}

/// The non-dotless version of a dotless character that can be used with the
/// `dtls` OpenType feature.
pub fn try_dotless(c: char) -> Option<char> {
    match c {
        'ı' => Some('i'),
        'ȷ' => Some('j'),
        _ => None,
    }
}