1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
//! Implementation of the \@char function in KaTeX
//!
//! This module implements the \@char LaTeX command, which converts a decimal
//! number to the corresponding Unicode character. It is used internally by
//! the \char macro to create symbols from code points.
use crate::context::KatexContext;
use crate::define_function::{FunctionDefSpec, FunctionPropSpec};
use crate::parser::parse_node::{AnyParseNode, NodeType, ParseNode, ParseNodeTextOrd};
use crate::types::{ParseError, ParseErrorKind, TokenText};
/// Register the \@char function
pub fn define_char(ctx: &mut KatexContext) {
ctx.define_function(FunctionDefSpec {
node_type: Some(NodeType::TextOrd),
names: &["\\@char"],
props: FunctionPropSpec {
num_args: 1,
allowed_in_text: true,
..Default::default()
},
handler: Some(
|context, args: Vec<ParseNode>, _opt_args: Vec<Option<ParseNode>>| {
// Extract the first argument, which should be an ordgroup
let arg = &args[0];
let ParseNode::OrdGroup(ordgroup) = arg else {
return Err(ParseError::new(ParseErrorKind::CharArgumentMustBeOrdGroup));
};
// Concatenate all text from textord and mathord nodes in the group
let mut number_str = String::new();
for node in &ordgroup.body {
match node {
AnyParseNode::TextOrd(textord) => {
number_str.push_str(&textord.text);
}
AnyParseNode::MathOrd(mathord) => {
number_str.push_str(&mathord.text);
}
_ => {
return Err(ParseError::new(
ParseErrorKind::CharOrdGroupContentInvalid,
));
}
}
}
// Parse the number
let code_point: u32 = match number_str.parse() {
Ok(n) => n,
Err(_) => {
return Err(ParseError::new(ParseErrorKind::CharNonNumericArgument {
value: number_str.clone(),
}));
}
};
// Validate the code point range
if code_point > 0x10FFFF {
return Err(ParseError::new(ParseErrorKind::InvalidCharCodePoint {
code: number_str.clone(),
}));
}
// Convert code point to character(s)
let text = char::from_u32(code_point).ok_or_else(|| {
ParseError::new(ParseErrorKind::InvalidCharCodePoint {
code: number_str.clone(),
})
})?;
// Return a textord node with the character
Ok(ParseNode::TextOrd(ParseNodeTextOrd {
mode: context.parser.mode,
loc: context.loc(),
text: TokenText::from(text.to_string()),
}))
},
),
html_builder: None,
mathml_builder: None,
});
}