Skip to main content

ratex_parser/functions/
font.rs

1use std::collections::HashMap;
2
3use crate::error::ParseResult;
4use crate::functions::{define_function_full, ArgType, FunctionContext, FunctionSpec};
5use crate::parse_node::ParseNode;
6
7pub fn register(map: &mut HashMap<&'static str, FunctionSpec>) {
8    define_function_full(
9        map,
10        &[
11            "\\mathrm",
12            "\\mathit",
13            "\\mathbf",
14            "\\mathnormal",
15            "\\mathsfit",
16            "\\mathbb",
17            "\\mathcal",
18            "\\mathfrak",
19            "\\mathscr",
20            "\\mathsf",
21            "\\mathtt",
22            "\\Bbb",
23            "\\bold",
24            "\\frak",
25        ],
26        "font",
27        1,
28        0,
29        None,
30        true, // allowed_in_argument
31        false,
32        true,
33        false,
34        false,
35        handle_font,
36    );
37
38    // \boldsymbol and \bm produce mclass nodes
39    define_function_full(
40        map,
41        &["\\boldsymbol", "\\bm"],
42        "mclass",
43        1,
44        0,
45        None,
46        false,
47        false,
48        true,
49        false,
50        false,
51        handle_boldsymbol,
52    );
53
54    // \emph — emphasis (italic in text mode)
55    define_function_full(
56        map,
57        &["\\emph"],
58        "text",
59        1,
60        0,
61        Some(vec![ArgType::Text]),
62        false,
63        true,
64        true,
65        false,
66        false,
67        handle_emph,
68    );
69
70    // Old-style font switching commands: \rm, \sf, \tt, \bf, \it, \cal
71    define_function_full(
72        map,
73        &["\\rm", "\\sf", "\\tt", "\\bf", "\\it", "\\cal"],
74        "font",
75        0,
76        0,
77        None,
78        false,
79        true,
80        true,
81        false,
82        false,
83        handle_old_font,
84    );
85}
86
87fn handle_font(
88    ctx: &mut FunctionContext,
89    args: Vec<ParseNode>,
90    _opt_args: Vec<Option<ParseNode>>,
91) -> ParseResult<ParseNode> {
92    let body = ParseNode::normalize_argument(args.into_iter().next().unwrap());
93
94    let font_aliases: &[(&str, &str)] = &[
95        ("\\Bbb", "\\mathbb"),
96        ("\\bold", "\\mathbf"),
97        ("\\frak", "\\mathfrak"),
98        ("\\bm", "\\boldsymbol"),
99    ];
100
101    let mut func = ctx.func_name.clone();
102    for &(alias, target) in font_aliases {
103        if func == alias {
104            func = target.to_string();
105            break;
106        }
107    }
108
109    let font_name = func.strip_prefix('\\').unwrap_or(func.as_str()).to_string();
110
111    Ok(ParseNode::Font {
112        mode: ctx.parser.mode,
113        font: font_name,
114        body: Box::new(body),
115        loc: None,
116    })
117}
118
119fn handle_old_font(
120    ctx: &mut FunctionContext,
121    _args: Vec<ParseNode>,
122    _opt_args: Vec<Option<ParseNode>>,
123) -> ParseResult<ParseNode> {
124    let font_name = format!("math{}", &ctx.func_name[1..]);
125    let break_on = ctx.break_on_token_text.clone();
126    let body = ctx.parser.parse_expression(true, break_on.as_deref())?;
127
128    Ok(ParseNode::Font {
129        mode: ctx.parser.mode,
130        font: font_name,
131        body: Box::new(ParseNode::OrdGroup {
132            mode: ctx.parser.mode,
133            body,
134            semisimple: None,
135            loc: None,
136        }),
137        loc: None,
138    })
139}
140
141fn handle_boldsymbol(
142    ctx: &mut FunctionContext,
143    args: Vec<ParseNode>,
144    _opt_args: Vec<Option<ParseNode>>,
145) -> ParseResult<ParseNode> {
146    let body = args.into_iter().next().unwrap();
147
148    Ok(ParseNode::MClass {
149        mode: ctx.parser.mode,
150        mclass: "mord".to_string(),
151        body: vec![ParseNode::Font {
152            mode: ctx.parser.mode,
153            font: "boldsymbol".to_string(),
154            body: Box::new(body),
155            loc: None,
156        }],
157        is_character_box: false,
158        loc: None,
159    })
160}
161
162fn handle_emph(
163    ctx: &mut FunctionContext,
164    args: Vec<ParseNode>,
165    _opt_args: Vec<Option<ParseNode>>,
166) -> ParseResult<ParseNode> {
167    let body = args.into_iter().next().unwrap();
168    let body_vec = ParseNode::ord_argument(body);
169    Ok(ParseNode::Text {
170        mode: ctx.parser.mode,
171        body: body_vec,
172        font: Some("\\emph".to_string()),
173        loc: None,
174    })
175}