use phf::phf_map;
use crate::define_function::{FunctionDefSpec, FunctionPropSpec, normalize_argument};
use crate::dom_tree::HtmlDomNode;
use crate::functions::mclass::binrel_class;
use crate::mathml_tree::MathDomNode;
use crate::options::Options;
use crate::parser::parse_node::{
AnyParseNode, NodeType, ParseNode, ParseNodeFont, ParseNodeMclass, ParseNodeOrdGroup,
};
use crate::types::{ParseError, ParseErrorKind};
use crate::{KatexContext, build_html, build_mathml};
fn html_builder(
node: &ParseNode,
options: &Options,
ctx: &KatexContext,
) -> Result<HtmlDomNode, ParseError> {
let ParseNode::Font(font_node) = node else {
return Err(ParseError::new(ParseErrorKind::ExpectedNode {
node: NodeType::Font,
}));
};
let new_options = options.with_font(font_node.font.clone());
build_html::build_group(ctx, &font_node.body, &new_options, None)
}
fn mathml_builder(
node: &ParseNode,
options: &Options,
ctx: &KatexContext,
) -> Result<MathDomNode, ParseError> {
let ParseNode::Font(font_node) = node else {
return Err(ParseError::new(ParseErrorKind::ExpectedNode {
node: NodeType::Font,
}));
};
let new_options = options.with_font(font_node.font.clone());
build_mathml::build_group(ctx, &font_node.body, &new_options)
}
const FONT_ALIASES_MAP: phf::Map<&str, &str> = phf_map!(
"\\Bbb" => "\\mathbb",
"\\bold" => "\\mathbf",
"\\frak" => "\\mathfrak",
"\\bm" => "\\boldsymbol",
);
const FONT_NAMES: &[&str] = &[
"\\mathrm",
"\\mathit",
"\\mathbf",
"\\mathnormal",
"\\mathsfit",
"\\mathbb",
"\\mathcal",
"\\mathfrak",
"\\mathscr",
"\\mathsf",
"\\mathtt",
"\\Bbb",
"\\bold",
"\\frak",
];
pub fn define_font(ctx: &mut KatexContext) {
ctx.define_function(FunctionDefSpec {
node_type: Some(NodeType::Font),
names: FONT_NAMES,
props: FunctionPropSpec {
num_args: 1,
allowed_in_argument: true,
..Default::default()
},
handler: Some(|context, args, _opt_args| {
let body = normalize_argument(&args[0]);
let mut func = context.func_name.to_owned();
if let Some(replacement) = FONT_ALIASES_MAP.get(func.as_str()) {
func = (*replacement).to_owned();
}
let font = func[1..].to_string();
Ok(ParseNode::Font(ParseNodeFont {
mode: context.parser.mode,
loc: context.loc(),
font,
body: Box::new(body.clone()),
}))
}),
html_builder: Some(html_builder),
mathml_builder: Some(mathml_builder),
});
let bold_names = ["\\boldsymbol", "\\bm"];
ctx.define_function(FunctionDefSpec {
node_type: Some(NodeType::Mclass),
names: &bold_names,
props: FunctionPropSpec {
num_args: 1,
..Default::default()
},
handler: Some(|context, args, _opt_args| {
let body = &args[0];
let is_character_box = body.is_character_box()?;
let font_node = ParseNode::Font(ParseNodeFont {
mode: context.parser.mode,
loc: context.loc(),
font: "boldsymbol".to_owned(),
body: Box::new(body.clone()),
});
Ok(ParseNode::Mclass(ParseNodeMclass {
mode: context.parser.mode,
loc: context.loc(),
mclass: binrel_class(body),
body: vec![font_node],
is_character_box,
}))
}),
html_builder: None, mathml_builder: None,
});
ctx.define_function(FunctionDefSpec {
node_type: Some(NodeType::Font),
names: &["\\rm", "\\sf", "\\tt", "\\bf", "\\it", "\\cal"],
props: FunctionPropSpec {
num_args: 0,
allowed_in_text: true,
..Default::default()
},
handler: Some(|context, _args, _opt_args| {
let func_name = context.func_name;
let body = context
.parser
.parse_expression(true, context.break_on_token_text)?;
let style = format!("math{}", &func_name[1..]);
let ordgroup = AnyParseNode::OrdGroup(ParseNodeOrdGroup {
mode: context.parser.mode,
loc: context.loc(),
body,
semisimple: None,
});
Ok(ParseNode::Font(ParseNodeFont {
mode: context.parser.mode,
loc: context.loc(),
font: style,
body: Box::new(ordgroup),
}))
}),
html_builder: Some(html_builder),
mathml_builder: Some(mathml_builder),
});
}