use crate::ParseError;
use crate::build_common::mathsym;
use crate::build_mathml::{get_variant, make_text};
use crate::context::KatexContext;
use crate::dom_tree::HtmlDomNode;
use crate::mathml_tree::{MathDomNode, MathNode, MathNodeType};
use crate::options::Options;
use crate::parser::parse_node::{AnyParseNode, NodeType};
use crate::symbols::Atom;
use crate::types::ClassList;
use crate::types::ParseErrorKind;
pub fn define_symbols_op(ctx: &mut KatexContext) {
ctx.define_function_builders(
NodeType::Atom,
Some(atom_html_builder),
Some(atom_mathml_builder),
);
}
fn atom_html_builder(
node: &AnyParseNode,
options: &Options,
ctx: &KatexContext,
) -> Result<HtmlDomNode, ParseError> {
let (text, mode, family) = match node {
AnyParseNode::Atom(atom) => (&atom.text, atom.mode, atom.family),
_ => {
return Err(ParseError::new(ParseErrorKind::ExpectedNode {
node: NodeType::Atom,
}));
}
};
let class_name = format!("m{}", family.as_ref());
let classes = ClassList::Owned(vec![class_name.into()]);
Ok(mathsym(ctx, text, mode, options, classes)?.into())
}
fn atom_mathml_builder(
node: &AnyParseNode,
options: &Options,
ctx: &KatexContext,
) -> Result<MathDomNode, ParseError> {
let (text, mode, family) = match node {
AnyParseNode::Atom(atom) => (&atom.text, atom.mode, atom.family),
_ => {
return Err(ParseError::new(ParseErrorKind::ExpectedNode {
node: NodeType::Atom,
}));
}
};
let text_node = make_text(text, mode, Some(options), &ctx.symbols);
let mut mo_node = MathNode::builder()
.node_type(MathNodeType::Mo)
.children(vec![MathDomNode::Text(text_node)])
.build();
match family {
Atom::Bin => {
if let Some(variant) = get_variant(ctx, node, options)?
&& variant == "bold-italic"
{
mo_node.set_attribute("mathvariant", variant);
}
}
Atom::Punct => {
mo_node.set_attribute("separator", "true");
}
Atom::Open | Atom::Close => {
mo_node.set_attribute("stretchy", "false");
}
_ => {
}
}
Ok(MathDomNode::Math(mo_node))
}
#[cfg(test)]
mod tests {
use super::*;
use crate::options::Options;
use crate::parser::parse_node::ParseNodeAtom;
use crate::style;
use crate::symbols::Atom;
use crate::types::Mode;
fn create_test_options() -> Options {
Options::builder()
.style(style::TEXT)
.phantom(false)
.max_size(1_000_000.0)
.min_rule_thickness(0.04)
.build()
}
fn create_test_context() -> KatexContext {
let mut ctx = KatexContext::default();
define_symbols_op(&mut ctx);
ctx
}
#[test]
fn test_atom_html_builder_bin() {
let ctx = create_test_context();
let options = create_test_options();
let node = AnyParseNode::Atom(ParseNodeAtom {
mode: Mode::Math,
loc: None,
text: "+".into(),
family: Atom::Bin,
});
let result = atom_html_builder(&node, &options, &ctx);
assert!(result.is_ok());
if let Ok(HtmlDomNode::Symbol(_)) = result {
} else {
panic!("Expected Symbol node");
}
}
#[test]
fn test_atom_mathml_builder_bin() {
let ctx = create_test_context();
let options = create_test_options();
let node = AnyParseNode::Atom(ParseNodeAtom {
mode: Mode::Math,
loc: None,
text: "+".into(),
family: Atom::Bin,
});
let result = atom_mathml_builder(&node, &options, &ctx);
assert!(result.is_ok());
if let Ok(MathDomNode::Math(math_node)) = result {
assert_eq!(math_node.node_type, MathNodeType::Mo);
assert_eq!(math_node.children.len(), 1);
} else {
panic!("Expected Math node");
}
}
#[test]
fn test_atom_mathml_builder_punct() {
let ctx = create_test_context();
let options = create_test_options();
let node = AnyParseNode::Atom(ParseNodeAtom {
mode: Mode::Math,
loc: None,
text: ",".into(),
family: Atom::Punct,
});
let result = atom_mathml_builder(&node, &options, &ctx);
assert!(result.is_ok());
if let Ok(MathDomNode::Math(math_node)) = result {
assert_eq!(math_node.node_type, MathNodeType::Mo);
assert_eq!(math_node.children.len(), 1);
assert_eq!(
math_node.attributes.get("separator"),
Some(&"true".to_owned())
);
} else {
panic!("Expected Math node");
}
}
#[test]
fn test_atom_mathml_builder_open() {
let ctx = create_test_context();
let options = create_test_options();
let node = AnyParseNode::Atom(ParseNodeAtom {
mode: Mode::Math,
loc: None,
text: "(".into(),
family: Atom::Open,
});
let result = atom_mathml_builder(&node, &options, &ctx);
assert!(result.is_ok());
if let Ok(MathDomNode::Math(math_node)) = result {
assert_eq!(math_node.node_type, MathNodeType::Mo);
assert_eq!(math_node.children.len(), 1);
assert_eq!(
math_node.attributes.get("stretchy"),
Some(&"false".to_owned())
);
} else {
panic!("Expected Math node");
}
}
#[test]
fn test_atom_mathml_builder_close() {
let ctx = create_test_context();
let options = create_test_options();
let node = AnyParseNode::Atom(ParseNodeAtom {
mode: Mode::Math,
loc: None,
text: ")".into(),
family: Atom::Close,
});
let result = atom_mathml_builder(&node, &options, &ctx);
assert!(result.is_ok());
if let Ok(MathDomNode::Math(math_node)) = result {
assert_eq!(math_node.node_type, MathNodeType::Mo);
assert_eq!(math_node.children.len(), 1);
assert_eq!(
math_node.attributes.get("stretchy"),
Some(&"false".to_owned())
);
} else {
panic!("Expected Math node");
}
}
#[test]
fn test_atom_mathml_builder_rel() {
let ctx = create_test_context();
let options = create_test_options();
let node = AnyParseNode::Atom(ParseNodeAtom {
mode: Mode::Math,
loc: None,
text: "=".into(),
family: Atom::Rel,
});
let result = atom_mathml_builder(&node, &options, &ctx);
assert!(result.is_ok());
if let Ok(MathDomNode::Math(math_node)) = result {
assert_eq!(math_node.node_type, MathNodeType::Mo);
assert_eq!(math_node.children.len(), 1);
assert!(!math_node.attributes.contains_key("separator"));
assert!(!math_node.attributes.contains_key("stretchy"));
} else {
panic!("Expected Math node");
}
}
}