Skip to main content

katex_parser/
unicode_font.rs

1/// Returns the Unicode mathematical alphanumeric character for a given font
2/// style and ASCII letter/digit, or `None` when no mapping exists.
3///
4/// Font styles that match LaTeX commands:
5/// - `mathbf` / `boldsymbol` — Bold / Bold Italic
6/// - `mathit` — Italic
7/// - `mathbb` — Double-struck (blackboard bold)
8/// - `mathcal` — Calligraphic / Script
9/// - `mathfrak` — Fraktur
10/// - `mathscr` — Script (rsfs)
11/// - `mathsf` — Sans-serif
12/// - `mathtt` — Monospace
13/// - `mathsfit` — Sans-serif Italic
14/// - `mathrm` / `mathnormal` — Roman (identity, not handled here) / Italic
15pub fn unicode_font_character(font: &str, ch: &str) -> Option<String> {
16    let mut chars = ch.chars();
17    let c = chars.next()?;
18    if chars.next().is_some() {
19        return None;
20    }
21    let cp = match font {
22        "mathbf" | "boldsymbol" => {
23            let base = if font == "boldsymbol" { 0x1D468 } else { 0x1D400 };
24            let greek_cap = if font == "boldsymbol" { 0x1D71C } else { 0x1D6A8 };
25            let greek_small = if font == "boldsymbol" { 0x1D736 } else { 0x1D6C2 };
26            let greek_symbols = if font == "boldsymbol" { 0x1D750 } else { 0x1D6DC };
27            let cd = c as u32;
28            match c {
29                'A'..='Z' => Some(base + cd - 'A' as u32),
30                'a'..='z' => Some(base + 26 + cd - 'a' as u32),
31                '0'..='9' if font != "boldsymbol" => Some(0x1D7CE + cd - '0' as u32),
32                'Α'..='Ω' => Some(greek_cap + cd - 'Α' as u32),
33                'α'..='ω' => Some(greek_small + cd - 'α' as u32),
34                'ϵ' => Some(greek_symbols),
35                'ϑ' => Some(greek_symbols + 1),
36                'ϰ' => Some(greek_symbols + 2),
37                'ϕ' => Some(greek_symbols + 3),
38                'ϱ' => Some(greek_symbols + 4),
39                'ϖ' => Some(greek_symbols + 5),
40                '∇' => Some(greek_cap + 0x19),
41                '∂' => Some(greek_small + 0x19),
42                _ => None,
43            }
44        }
45        "mathit" | "mathnormal" => {
46            let cd = c as u32;
47            match c {
48                'A'..='Z' => Some(0x1D434 + cd - 'A' as u32),
49                'a'..='z' => Some(0x1D44E + cd - 'a' as u32),
50                'Α'..='Ω' => Some(0x1D6E2 + cd - 'Α' as u32),
51                'α'..='ω' => Some(0x1D6FC + cd - 'α' as u32),
52                'ϵ' => Some(0x1D716),
53                'ϑ' => Some(0x1D717),
54                'ϰ' => Some(0x1D718),
55                'ϕ' => Some(0x1D719),
56                'ϱ' => Some(0x1D71A),
57                'ϖ' => Some(0x1D71B),
58                '∇' => Some(0x1D6FB),
59                '∂' => Some(0x1D715),
60                _ => None,
61            }
62        }
63        "mathsf" => {
64            let cd = c as u32;
65            match c {
66                'A'..='Z' => Some(0x1D5A0 + cd - 'A' as u32),
67                'a'..='z' => Some(0x1D5BA + cd - 'a' as u32),
68                '0'..='9' => Some(0x1D7E2 + cd - '0' as u32),
69                _ => None,
70            }
71        }
72        "mathtt" => {
73            let cd = c as u32;
74            match c {
75                'A'..='Z' => Some(0x1D670 + cd - 'A' as u32),
76                'a'..='z' => Some(0x1D68A + cd - 'a' as u32),
77                '0'..='9' => Some(0x1D7F6 + cd - '0' as u32),
78                _ => None,
79            }
80        }
81        "mathbb" => {
82            let cd = c as u32;
83            match c {
84                'A' => Some(0x1D538),
85                'B' => Some(0x1D539),
86                'C' => Some(0x2102),
87                'D' => Some(0x1D53B),
88                'E' => Some(0x1D53C),
89                'F' => Some(0x1D53D),
90                'G' => Some(0x1D53E),
91                'H' => Some(0x210D),
92                'I' => Some(0x1D540),
93                'J' => Some(0x1D541),
94                'K' => Some(0x1D542),
95                'L' => Some(0x1D543),
96                'M' => Some(0x1D544),
97                'N' => Some(0x2115),
98                'O' => Some(0x1D546),
99                'P' => Some(0x2119),
100                'Q' => Some(0x211A),
101                'R' => Some(0x211D),
102                'S' => Some(0x1D54A),
103                'T' => Some(0x1D54B),
104                'U' => Some(0x1D54C),
105                'V' => Some(0x1D54D),
106                'W' => Some(0x1D54E),
107                'X' => Some(0x1D54F),
108                'Y' => Some(0x1D550),
109                'Z' => Some(0x2124),
110                'a'..='z' => Some(0x1D552 + cd - 'a' as u32),
111                '0'..='9' => Some(0x1D7D8 + cd - '0' as u32),
112                _ => None,
113            }
114        }
115        "mathcal" => match c {
116            'A' => Some(0x1D49C),
117            'B' => Some(0x212C),
118            'C' => Some(0x1D49E),
119            'D' => Some(0x1D49F),
120            'E' => Some(0x2130),
121            'F' => Some(0x2131),
122            'G' => Some(0x1D4A2),
123            'H' => Some(0x210B),
124            'I' => Some(0x2110),
125            'J' => Some(0x1D4A5),
126            'K' => Some(0x1D4A6),
127            'L' => Some(0x2112),
128            'M' => Some(0x2133),
129            'N' => Some(0x1D4A9),
130            'O' => Some(0x1D4AA),
131            'P' => Some(0x1D4AB),
132            'Q' => Some(0x1D4AC),
133            'R' => Some(0x211B),
134            'S' => Some(0x1D4AE),
135            'T' => Some(0x1D4AF),
136            'U' => Some(0x1D4B0),
137            'V' => Some(0x1D4B1),
138            'W' => Some(0x1D4B2),
139            'X' => Some(0x1D4B3),
140            'Y' => Some(0x1D4B4),
141            'Z' => Some(0x1D4B5),
142            _ => None,
143        },
144        "mathfrak" => {
145            let cd = c as u32;
146            match c {
147                'A' => Some(0x1D504),
148                'B' => Some(0x1D505),
149                'C' => Some(0x212D),
150                'D' => Some(0x1D507),
151                'E' => Some(0x1D508),
152                'F' => Some(0x1D509),
153                'G' => Some(0x1D50A),
154                'H' => Some(0x210C),
155                'I' => Some(0x2111),
156                'J' => Some(0x1D50D),
157                'K' => Some(0x1D50E),
158                'L' => Some(0x1D50F),
159                'M' => Some(0x1D510),
160                'N' => Some(0x1D511),
161                'O' => Some(0x1D512),
162                'P' => Some(0x1D513),
163                'Q' => Some(0x1D514),
164                'R' => Some(0x211C),
165                'S' => Some(0x1D516),
166                'T' => Some(0x1D517),
167                'U' => Some(0x1D518),
168                'V' => Some(0x1D519),
169                'W' => Some(0x1D51A),
170                'X' => Some(0x1D51B),
171                'Y' => Some(0x1D51C),
172                'Z' => Some(0x2128),
173                'a'..='z' => Some(0x1D51E + cd - 'a' as u32),
174                _ => None,
175            }
176        }
177        "mathscr" => {
178            let cd = c as u32;
179            match c {
180                'A'..='Z' => Some(0x1D4D0 + cd - 'A' as u32),
181                'a'..='z' => Some(0x1D4EA + cd - 'a' as u32),
182                _ => None,
183            }
184        }
185        "mathsfit" => {
186            let cd = c as u32;
187            match c {
188                'A'..='Z' => Some(0x1D608 + cd - 'A' as u32),
189                'a'..='z' => Some(0x1D622 + cd - 'a' as u32),
190                _ => None,
191            }
192        }
193        _ => None,
194    };
195    cp.map(|cp| char::from_u32(cp).unwrap().to_string())
196}