Skip to main content

katex_parser/anvil/
common.rs

1//! Shared, backend-agnostic helpers for the rendering backends (mathml,
2//! typst, unicode). These encode KaTeX facts that each backend would otherwise
3//! re-implement with drift risk: symbol resolution, null delimiters, control
4//! sequence names, font sizes, unit conversions, and style selection.
5
6use crate::ast::{Measurement, ParseNode, StyleLevel};
7use crate::symbol_registry::unicode_symbol;
8
9/// Resolves a TeX command name or raw Unicode string to its Unicode rendering
10/// via the parser's symbol registry, falling back to the input itself.
11pub fn resolve_symbol(text: &str) -> String {
12    unicode_symbol(text).unwrap_or_else(|| text.to_string())
13}
14
15/// True for the null delimiter `.` used by `\left.` / `\right.`, which
16/// renders as nothing in every backend.
17pub fn is_null_delimiter(text: &str) -> bool {
18    text == "."
19}
20
21/// Strips the leading backslash from a control-sequence label (`\sin` ->
22/// `sin`). Labels without a backslash are returned unchanged.
23pub fn command_name(label: &str) -> String {
24    label.strip_prefix('\\').unwrap_or(label).to_string()
25}
26
27/// The KaTeX font-size multiplier table (`\tiny` .. `\Huge`, including
28/// `\sixptsize`), indexed by the 1-based `Sizing.size` field. Sizes outside
29/// the table fall back to 1.0 (`\normalsize`).
30pub fn katex_size_multiplier(size: usize) -> f64 {
31    const SIZES: [f64; 11] = [0.5, 0.6, 0.7, 0.8, 0.9, 1.0, 1.2, 1.44, 1.728, 2.074, 2.488];
32    size.checked_sub(1)
33        .and_then(|index| SIZES.get(index))
34        .copied()
35        .unwrap_or(1.0)
36}
37
38/// Converts a measurement to an em length: `em` passes through, `mu` uses
39/// the 18 mu per em ratio, and `ex` the standard 0.5 x-height ratio. Returns
40/// `None` for units with no fixed em relation (`fill`, `pt`, ...).
41pub fn em_value(measurement: &Measurement) -> Option<f64> {
42    match measurement.unit.as_str() {
43        "em" => Some(measurement.number),
44        "mu" => Some(measurement.number / 18.0),
45        "ex" => Some(measurement.number * 0.5),
46        _ => None,
47    }
48}
49
50/// Selects the body of a `\mathchoice` node for the current style level.
51pub fn math_choice_variant(
52    display: &[ParseNode],
53    text: &[ParseNode],
54    script: &[ParseNode],
55    scriptscript: &[ParseNode],
56    style: StyleLevel,
57) -> Vec<ParseNode> {
58    match style {
59        StyleLevel::DisplayStyle => display.to_vec(),
60        StyleLevel::TextStyle => text.to_vec(),
61        StyleLevel::ScriptStyle => script.to_vec(),
62        StyleLevel::ScriptScriptStyle => scriptscript.to_vec(),
63    }
64}