use crate::build_common::{
VListChild, VListElem, VListParam, make_fragment, make_span, make_v_list,
};
use crate::define_function::{FunctionDefSpec, FunctionPropSpec, ord_argument};
use crate::dom_tree::HtmlDomNode;
use crate::mathml_tree::{MathDomNode, MathNode, MathNodeType};
use crate::options::Options;
use crate::parser::parse_node::{
AnyParseNode, NodeType, ParseNodeHphantom, ParseNodePhantom, ParseNodeVphantom,
};
use crate::types::{ParseError, ParseErrorKind};
use crate::{ClassList, build_html, build_mathml};
pub fn define_phantom(ctx: &mut crate::KatexContext) {
ctx.define_function(FunctionDefSpec {
node_type: Some(NodeType::Phantom),
names: &["\\phantom"],
props: FunctionPropSpec {
num_args: 1,
allowed_in_text: true,
..Default::default()
},
handler: Some(|context, args, _opt_args| {
let body = args[0].clone();
Ok(AnyParseNode::Phantom(ParseNodePhantom {
mode: context.parser.mode,
loc: context.loc(),
body: ord_argument(&body),
}))
}),
html_builder: Some(html_builder_phantom),
mathml_builder: Some(mathml_builder_phantom),
});
ctx.define_function(FunctionDefSpec {
node_type: Some(NodeType::Hphantom),
names: &["\\hphantom"],
props: FunctionPropSpec {
num_args: 1,
allowed_in_text: true,
..Default::default()
},
handler: Some(|context, args, _opt_args| {
let body = args[0].clone();
Ok(AnyParseNode::Hphantom(ParseNodeHphantom {
mode: context.parser.mode,
loc: context.loc(),
body: Box::new(body),
}))
}),
html_builder: Some(html_builder_hphantom),
mathml_builder: Some(mathml_builder_hphantom),
});
ctx.define_function(FunctionDefSpec {
node_type: Some(NodeType::Vphantom),
names: &["\\vphantom"],
props: FunctionPropSpec {
num_args: 1,
allowed_in_text: true,
..Default::default()
},
handler: Some(|context, args, _opt_args| {
let body = args[0].clone();
Ok(AnyParseNode::Vphantom(ParseNodeVphantom {
mode: context.parser.mode,
loc: context.loc(),
body: Box::new(body),
}))
}),
html_builder: Some(html_builder_vphantom),
mathml_builder: Some(mathml_builder_vphantom),
});
}
fn html_builder_phantom(
node: &AnyParseNode,
options: &Options,
ctx: &crate::KatexContext,
) -> Result<HtmlDomNode, ParseError> {
let AnyParseNode::Phantom(phantom_node) = node else {
return Err(ParseError::new(ParseErrorKind::ExpectedNode {
node: NodeType::Phantom,
}));
};
let phantom_options = options.with_phantom();
let elements = build_html::build_expression(
ctx,
&phantom_node.body,
&phantom_options,
build_html::GroupType::False,
(None, None),
)?;
Ok(make_fragment(elements).into())
}
fn html_builder_hphantom(
node: &AnyParseNode,
options: &Options,
ctx: &crate::KatexContext,
) -> Result<HtmlDomNode, ParseError> {
let AnyParseNode::Hphantom(hphantom_node) = node else {
return Err(ParseError::new(ParseErrorKind::ExpectedNode {
node: NodeType::Hphantom,
}));
};
let phantom_options = options.with_phantom();
let inner = build_html::build_group(ctx, &hphantom_node.body, &phantom_options, None)?;
let mut node_span = make_span(ClassList::Empty, vec![inner], None, None);
node_span.height = 0.0;
node_span.depth = 0.0;
for child in &mut node_span.children {
if let Some(h) = child.height_mut() {
*h = 0.0;
}
if let Some(d) = child.depth_mut() {
*d = 0.0;
}
}
let vlist = make_v_list(
VListParam::FirstBaseline {
children: vec![VListChild::Elem(
VListElem::builder().elem(node_span.into()).build().into(),
)],
},
options,
)?;
Ok(make_span("mord", vec![vlist.into()], Some(options), None).into())
}
fn html_builder_vphantom(
node: &AnyParseNode,
options: &Options,
ctx: &crate::KatexContext,
) -> Result<HtmlDomNode, ParseError> {
let AnyParseNode::Vphantom(vphantom_node) = node else {
return Err(ParseError::new(ParseErrorKind::ExpectedNode {
node: NodeType::Vphantom,
}));
};
let phantom_options = options.with_phantom();
let inner = build_html::build_group(ctx, &vphantom_node.body, &phantom_options, None)?;
let inner_span = make_span("inner", vec![inner], None, None);
let fix = make_span("fix", vec![], None, None);
Ok(make_span(
ClassList::Const(&["mord", "rlap"]),
vec![inner_span.into(), fix.into()],
Some(options),
None,
)
.into())
}
fn mathml_builder_phantom(
node: &AnyParseNode,
options: &Options,
ctx: &crate::KatexContext,
) -> Result<MathDomNode, ParseError> {
let AnyParseNode::Phantom(phantom_node) = node else {
return Err(ParseError::new(ParseErrorKind::ExpectedNode {
node: NodeType::Phantom,
}));
};
let inner = build_mathml::build_expression(ctx, &phantom_node.body, options, None)?;
Ok(MathNode::builder()
.node_type(MathNodeType::Mphantom)
.children(inner)
.build()
.into())
}
fn mathml_builder_hphantom(
node: &AnyParseNode,
options: &Options,
ctx: &crate::KatexContext,
) -> Result<MathDomNode, ParseError> {
let AnyParseNode::Hphantom(hphantom_node) = node else {
return Err(ParseError::new(ParseErrorKind::ExpectedNode {
node: NodeType::Hphantom,
}));
};
let inner =
build_mathml::build_expression(ctx, &ord_argument(&hphantom_node.body), options, None)?;
let phantom = MathNode::builder()
.node_type(MathNodeType::Mphantom)
.children(inner)
.build();
let mut node = MathNode::builder()
.node_type(MathNodeType::Mpadded)
.children(vec![MathDomNode::Math(phantom)])
.build();
node.attributes.extend([
("height".to_owned(), "0px".to_owned()),
("depth".to_owned(), "0px".to_owned()),
]);
Ok(MathDomNode::Math(node))
}
fn mathml_builder_vphantom(
node: &AnyParseNode,
options: &Options,
ctx: &crate::KatexContext,
) -> Result<MathDomNode, ParseError> {
let AnyParseNode::Vphantom(vphantom_node) = node else {
return Err(ParseError::new(ParseErrorKind::ExpectedNode {
node: NodeType::Vphantom,
}));
};
let inner =
build_mathml::build_expression(ctx, &ord_argument(&vphantom_node.body), options, None)?;
let phantom = MathNode::builder()
.node_type(MathNodeType::Mphantom)
.children(inner)
.build();
let mut node = MathNode::builder()
.node_type(MathNodeType::Mpadded)
.children(vec![MathDomNode::Math(phantom)])
.build();
node.attributes.insert("width".to_owned(), "0px".to_owned());
Ok(MathDomNode::Math(node))
}