rustla/
utf8_to_latex.rs

1/*!
2This file contains a mapping between a subset of UTF-8 to LaTeX commands.
3
4Copyright © 2020 Santtu Söderholm
5*/
6
7use lazy_static::lazy_static;
8use std::collections::HashMap;
9
10/// A function for converting a given `&str` (which is valid UTF-8)
11/// into a valid LaTeX string. Some more exotic symbols might require
12/// a specific LaTeX package for the resulting object code to parse without errors,
13/// which is *not* taken into account by this function.
14///
15/// If not conversion exists, adds the unicode scalar into the string unchanged.
16pub fn unicode_math_to_latex(utf_str: &str) -> String {
17    let source_char_count = utf_str.chars().count();
18    let mut latex_string = String::with_capacity(source_char_count);
19
20    for c in utf_str.chars() {
21        if let Some(latex_str) = UTF8_MATH_TO_LATEX_MAP.get(&c) {
22            latex_string += latex_str;
23        } else {
24            latex_string.push(c);
25        }
26    }
27
28    latex_string
29}
30
31/// Escapes any non-text category LaTeX characters in a given `&str`,
32/// so as to allow the generated `.tex` document to compile if it has
33/// control characters such as underscores in a text node.
34///
35/// For example, `'_' ↦ "\_"` and `'@' ↦ "\@"`. If a character is not recognized as a control character,
36/// it is added to the generated `String` as is.
37pub fn unicode_text_to_latex(utf_str: &str) -> String {
38    let source_char_count = utf_str.chars().count();
39    let mut latex_string = String::with_capacity(source_char_count);
40    let mut char_iter = utf_str.chars().peekable();
41
42    while let Some(c1) = char_iter.next() {
43        let c1 = if c1 == '\\' {
44            if let Some(c2) = char_iter.peek() {
45                char_iter.next().unwrap()
46            } else {
47                c1
48            }
49        } else {
50            c1
51        };
52
53        let space_based_on_next_char = if let Some(c) = char_iter.peek() {
54            if (*c).is_whitespace()
55                || *c == '\\'
56                || ! (*c).is_ascii_alphabetic() {
57                ""
58            } else {
59                " "
60            }
61        } else {
62            ""
63        };
64
65        if let Some(latex_str) = UTF8_TEXT_TO_LATEX_MAP.get(&c1) {
66            latex_string = latex_string + latex_str + space_based_on_next_char;
67        } else {
68            latex_string.push(c1);
69        }
70    }
71
72    latex_string
73}
74
75/// An enumeration of the different TeX character categories.
76enum TeXCategory {
77    Escape,
78    StartGroup,
79    EndGroup,
80    MathShift,
81    AlignmentTab,
82    EndOfLine,
83    MacroParameter,
84    SuperScript,
85    SubScript,
86    Ignored,
87    Spacer,
88    Letter,
89    Other,
90    Active,
91    Comment,
92    Invalid,
93}
94
95impl TeXCategory {
96    /// Returns an array slice of the unicode scalars known to belong to the given category.
97    fn symbol_table(&self) -> &[char] {
98        match self {
99            Self::Escape => &['\\'],
100            Self::StartGroup => &['{'],
101            Self::EndGroup => &['}'],
102            Self::MathShift => &['$'],
103            Self::AlignmentTab => &['&'],
104            Self::EndOfLine => &['\r'],
105            Self::MacroParameter => &['#'],
106            Self::SuperScript => &['^'],
107            Self::SubScript => &['_'],
108            Self::Ignored => &['\u{0}'],
109            Self::Spacer => &[' ', '\t'],
110            Self::Letter => &[],
111            Self::Other => &[
112                '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ',', '.', ';', '?', '"',
113            ],
114            Self::Active => &[],
115            Self::Comment => &['%'],
116            Self::Invalid => &['\u{127}'],
117        }
118    }
119}
120
121lazy_static! {
122
123  /// A mapping of certain utf8 scalars to LaTeX strings.
124  ///
125  /// source: http://milde.users.sourceforge.net/LUCR/Math/unimathsymbols.html, 2020-09-15
126  static ref UTF8_MATH_TO_LATEX_MAP: HashMap<char, &'static str> = {
127    let mut map = HashMap::new();
128
129    // Basic Latin
130    map.insert('\u{23}', r#"\#"#);
131    map.insert('\u{24}', r#"\$"#);
132    map.insert('\u{25}', r#"\%"#);
133    //map.insert('\u{26}', r#"\&"#);
134    // map.insert('\u{5c}', r#"\backslash"#);
135    // map.insert('\u{7b}', r#"\{"#);
136    // map.insert('\u{7d}', r#"\}"#);
137    map.insert('\u{7e}', r#"\sim"#);
138
139    // Latin 1 Supplement
140    map.insert('\u{a2}', r#"\cent"#);
141    map.insert('\u{a3}', r#"\pounds"#);
142    map.insert('\u{a5}', r#"\yen"#);
143    map.insert('\u{a8}', r#"\spddot"#);
144    map.insert('\u{ac}', r#"\neg"#);
145    map.insert('\u{b1}', r#"\pm"#);
146    map.insert('\u{b5}', r#"\mathrm{\mu}"#);
147    map.insert('\u{b7}', r#"\cdot"#);
148    map.insert('\u{d7}', r#"\times"#);
149    map.insert('\u{f7}', r#"\eth"#);
150
151    // Combining diacritics
152    map.insert('\u{251}', r#"\alpha"#);
153    map.insert('\u{300}', r#"\grave"#);
154    map.insert('\u{301}', r#"\acute"#);
155    map.insert('\u{302}', r#"\hat"#);
156    map.insert('\u{303}', r#"\tilde"#);
157    map.insert('\u{304}', r#"\bar"#);
158    map.insert('\u{305}', r#"\overline"#);
159    map.insert('\u{306}', r#"\breve"#);
160    map.insert('\u{307}', r#"\dot"#);
161    map.insert('\u{308}', r#"\ddot"#);
162    map.insert('\u{30a}', r#"\mathring"#);
163    map.insert('\u{30c}', r#"\check"#);
164    //
165    map.insert('\u{330}', r#"\utilde"#);
166    map.insert('\u{331}', r#"\underbar"#);
167    map.insert('\u{332}', r#"\underline"#);
168    map.insert('\u{338}', r#"\not"#);
169
170
171    // Greek and Coptic
172    map.insert('\u{391}', r#"A"#);
173    map.insert('\u{392}', r#"B"#);
174    map.insert('\u{393}', r#"\Gamma"#);
175    map.insert('\u{394}', r#"\Delta"#);
176    map.insert('\u{395}', r#"E"#);
177    map.insert('\u{396}', r#"Z"#);
178    map.insert('\u{397}', r#"H"#);
179    map.insert('\u{398}', r#"\Theta"#);
180    map.insert('\u{399}', r#"I"#);
181    map.insert('\u{39a}', r#"K"#);
182    map.insert('\u{39b}', r#"\Lambda"#);
183    map.insert('\u{39c}', r#"M"#);
184    map.insert('\u{39d}', r#"N"#);
185    map.insert('\u{39e}', r#"\Xi"#);
186    map.insert('\u{39f}', r#"O"#);
187    map.insert('\u{3a0}', r#"\Pi"#);
188    map.insert('\u{3a1}', r#"P"#);
189    map.insert('\u{3a3}', r#"\Sigma"#);
190    map.insert('\u{3a4}', r#"T"#);
191    map.insert('\u{3a5}', r#"\Upsilon"#);
192    map.insert('\u{3a6}', r#"\Phi"#);
193    map.insert('\u{3a7}', r#"X"#);
194    map.insert('\u{3a8}', r#"\Psi"#);
195    map.insert('\u{3a8}', r#"\Omega"#);
196    map.insert('\u{3b1}', r#"\alpha"#);
197    map.insert('\u{3b2}', r#"\beta"#);
198    map.insert('\u{3b3}', r#"\gamma"#);
199    map.insert('\u{3b4}', r#"\delta"#);
200    map.insert('\u{3b5}', r#"\varepsilon"#);
201    map.insert('\u{3b6}', r#"\zeta"#);
202    map.insert('\u{3b7}', r#"\eta"#);
203    map.insert('\u{3b8}', r#"\theta"#);
204    map.insert('\u{3b9}', r#"\iota"#);
205    map.insert('\u{3ba}', r#"\kappa"#);
206    map.insert('\u{3bb}', r#"\lambda"#);
207    map.insert('\u{3bc}', r#"\mu"#);
208    map.insert('\u{3bd}', r#"\nu"#);
209    map.insert('\u{3be}', r#"\xi"#);
210    map.insert('\u{3bf}', r#"o"#);
211    map.insert('\u{3c0}', r#"\pi"#);
212    map.insert('\u{3c1}', r#"\rho"#);
213    map.insert('\u{3c2}', r#"\varsigma"#);
214    map.insert('\u{3c3}', r#"\sigma"#);
215    map.insert('\u{3c4}', r#"\tau"#);
216    map.insert('\u{3c5}', r#"\upsilon"#);
217    map.insert('\u{3c6}', r#"\varphi"#);
218    map.insert('\u{3c7}', r#"\chi"#);
219    map.insert('\u{3c8}', r#"\psi"#);
220    map.insert('\u{3c9}', r#"\omega"#);
221    map.insert('\u{3d0}', r#"\varbeta"#);
222    map.insert('\u{3d1}', r#"\vartheta"#);
223    map.insert('\u{3d2}', r#"\Upsilon"#); // actually \mathrm{\Upsilon}
224    map.insert('\u{3d5}', r#"\phi"#);
225    map.insert('\u{3d6}', r#"\varpi"#);
226    map.insert('\u{3d8}', r#"\Qoppa"#);
227    map.insert('\u{3d9}', r#"\qoppa"#);
228    map.insert('\u{3da}', r#"\Stigma"#);
229    map.insert('\u{3db}', r#"\stigma"#);
230    map.insert('\u{3dc}', r#"\Digamma"#);
231    map.insert('\u{3dd}', r#"\digamma"#);
232    map.insert('\u{3de}', r#"\Koppa"#);
233    map.insert('\u{3df}', r#"\koppa"#);
234    map.insert('\u{3e0}', r#"\Sampi"#);
235    map.insert('\u{3e1}', r#"\sampi"#);
236    map.insert('\u{3f0}', r#"\varkappa"#);
237    map.insert('\u{3f1}', r#"\varrho"#);
238    map.insert('\u{3f4}', r#"\Theta"#); // actually \Vartheta
239    map.insert('\u{3f5}', r#"\epsilon"#);
240    map.insert('\u{3f6}', r#"\backepsilon"#);
241
242    // General punctuation
243    map.insert('\u{2000}', r#"\ "#);
244    map.insert('\u{2001}', r#"\quad"#);
245    map.insert('\u{2002}', r#"\qquad"#);
246    map.insert('\u{2003}', r#"\ "#);
247    map.insert('\u{2004}', r#"\ "#);
248    map.insert('\u{2005}', r#"\ "#);
249    map.insert('\u{2006}', r#"\ "#);
250    map.insert('\u{2007}', r#"\ "#);
251    map.insert('\u{2008}', r#"\ "#);
252    map.insert('\u{2009}', r#"\ "#);
253    map.insert('\u{200a}', r#"\ "#);
254    map.insert('\u{200a}', r#"\ "#);
255    map.insert('\u{2010}', r#"-"#);
256    map.insert('\u{2012}', r#"-"#);
257    map.insert('\u{2013}', r#"-"#);
258    map.insert('\u{2014}', r#"-"#);
259    map.insert('\u{2015}', r#"-"#);
260    map.insert('\u{2016}', r#"\Vert"#);
261    //
262    map.insert('\u{2020}', r#"\dagger"#);
263    map.insert('\u{2021}', r#"\\ddagger"#);
264    map.insert('\u{2022}', r#"\bullet"#);
265    //
266    map.insert('\u{2026}', r#"\ldots"#);
267    map.insert('\u{2032}', r#"\prime"#);
268    map.insert('\u{2033}', r#"\second"#);
269    map.insert('\u{2034}', r#"\third"#);
270    map.insert('\u{2035}', r#"\backprime"#);
271    //
272    map.insert('\u{203c}', r#"!!"#);
273    map.insert('\u{2040}', r#"\cat"#);
274    //
275    map.insert('\u{2044}', r#"/"#);
276    map.insert('\u{2047}', r#"??"#);
277    //
278    map.insert('\u{2052}', r#"\:"#);
279
280
281    // Super- and subscripts
282    map.insert('\u{207a}', r#"^{+}"#);
283    map.insert('\u{207b}', r#"^{-}"#);
284    map.insert('\u{207c}', r#"^{=}"#);
285    map.insert('\u{207d}', r#"^{(}"#);
286    map.insert('\u{207e}', r#"^{)}"#);
287    map.insert('\u{208a}', r#"^{+}"#);
288    map.insert('\u{208b}', r#"^{-}"#);
289    map.insert('\u{208c}', r#"^{=}"#);
290    map.insert('\u{208d}', r#"^{(}"#);
291    map.insert('\u{208e}', r#"^{)}"#);
292
293    // Letterlike symbols
294    map.insert('\u{2102}', r#"\mathbb{C}"#);
295    map.insert('\u{2107}', r#"\Euler"#);
296    map.insert('\u{210a}', r#"\mathcal{g}"#);
297    map.insert('\u{210b}', r#"\mathcal{H}"#);
298    map.insert('\u{210c}', r#"\mathfrak{H}"#);
299    map.insert('\u{210d}', r#"\mathbb{H}"#);
300    map.insert('\u{210e}', r#"h"#);
301    map.insert('\u{210f}', r#"\hslash"#);
302    map.insert('\u{2110}', r#"\mathcal{I}"#);
303    map.insert('\u{2111}', r#"\Im"#);
304    map.insert('\u{2112}', r#"\mathcal{L}"#);
305    map.insert('\u{2113}', r#"\ell"#);
306    map.insert('\u{2115}', r#"\mathbb{N}"#);
307    map.insert('\u{2118}', r#"\wp"#);
308    map.insert('\u{2119}', r#"\mathbb{P}"#);
309    map.insert('\u{211a}', r#"\mathbb{Q}"#);
310    map.insert('\u{211b}', r#"\mathcal{R}"#);
311    map.insert('\u{211c}', r#"\Re"#);
312    map.insert('\u{211d}', r#"\mathbb{R}"#);
313    map.insert('\u{2124}', r#"\mathbb{Z}"#);
314    map.insert('\u{2126}', r#"\Omega"#);
315    map.insert('\u{2127}', r#"\mho"#);
316    map.insert('\u{2128}', r#"\mathfrak{Z}"#);
317    //
318    map.insert('\u{212b}', r#"\Angstroem"#);
319    map.insert('\u{212c}', r#"\mathcal{B}"#);
320    map.insert('\u{212d}', r#"\mathfrak{C}"#);
321    map.insert('\u{212f}', r#"\mathcal{e}"#);
322    map.insert('\u{2130}', r#"\mathcal{E}"#);
323    map.insert('\u{2131}', r#"\mathcal{F}"#);
324    map.insert('\u{2132}', r#"\Finv"#);
325    map.insert('\u{2133}', r#"\mathcal{M}"#);
326    map.insert('\u{2134}', r#"\mathcal{o}"#);
327    map.insert('\u{2135}', r#"\aleph"#);
328    map.insert('\u{2136}', r#"\beth"#);
329    map.insert('\u{2137}', r#"\gimel"#);
330    map.insert('\u{2138}', r#"\daleth"#);
331    map.insert('\u{213c}', r#"\mathbb{pi}"#);
332    map.insert('\u{213d}', r#"\mathbb{gamma}"#);
333    map.insert('\u{213e}', r#"\mathbb{Gamma}"#);
334    map.insert('\u{213f}', r#"\mathbb{Pi}"#);
335    map.insert('\u{2140}', r#"\mathbb{Sigma}"#);
336    //
337    map.insert('\u{2144}', r#"\Yup"#);
338    map.insert('\u{2145}', r#"\CapitalDifferentialD"#);
339    map.insert('\u{2146}', r#"\DifferentialD"#);
340    map.insert('\u{2147}', r#"\ExponentialE"#);
341    map.insert('\u{2148}', r#"\ComplexI"#);
342    map.insert('\u{2149}', r#"\ComplexJ"#);
343    //
344    map.insert('\u{214b}', r#"\invamp"#);
345
346
347    // Arrows
348    map.insert('\u{2190}', r#"\leftarrow"#);
349    map.insert('\u{2191}', r#"\uparrow"#);
350    map.insert('\u{2192}', r#"\rightarrow"#);
351    map.insert('\u{2193}', r#"\downarrow"#);
352    map.insert('\u{2194}', r#"\leftrightarrow"#);
353    map.insert('\u{2195}', r#"\nwwnarrow"#);
354    map.insert('\u{2196}', r#"\nearrow"#);
355    map.insert('\u{2197}', r#"\searrow"#);
356    map.insert('\u{2198}', r#"\swarrow"#);
357    map.insert('\u{2199}', r#"\nleftarrow"#);
358    map.insert('\u{219a}', r#"\nrightarrow"#);
359    //
360    map.insert('\u{219e}', r#"\twoheadleftarrow"#);
361    //
362    map.insert('\u{21a0}', r#"\twoheadrightarrow"#);
363    //
364    map.insert('\u{21a2}', r#"\leftarrowtail"#);
365    map.insert('\u{21a3}', r#"\rightarrowtail"#);
366    map.insert('\u{21a4}', r#"\mapsfrom"#);
367    map.insert('\u{21a5}', r#"\MapsUp"#);
368    map.insert('\u{21a6}', r#"\mapsto"#);
369    map.insert('\u{21a7}', r#"\MapsDown"#);
370    //
371    map.insert('\u{21a9}', r#"\heekleftarrow"#);
372    map.insert('\u{21aa}', r#"\hookrightarrow"#);
373    map.insert('\u{21ab}', r#"\looparrowleft"#);
374    map.insert('\u{21ac}', r#"\looparrowright"#);
375    map.insert('\u{21ad}', r#"\leftrightsquigarrow"#);
376    map.insert('\u{21ae}', r#"\nleftrightarrow"#);
377    map.insert('\u{21af}', r#"\lightning"#);
378    //
379    map.insert('\u{21b6}', r#"\curvearrowleft"#);
380    map.insert('\u{21b7}', r#"\curvearrowright"#);
381    //
382    map.insert('\u{21ba}', r#"\circlearrowleft"#);
383    map.insert('\u{21bb}', r#"\circlearrowright"#);
384    map.insert('\u{21bc}', r#"\leftharpoonup"#);
385    map.insert('\u{21bd}', r#"\leftharpoondown"#);
386    map.insert('\u{21be}', r#"\upharpoonright"#);
387    map.insert('\u{21bf}', r#"\upharpoonleft"#);
388    map.insert('\u{21c0}', r#"\rightharpoonup"#);
389    map.insert('\u{21c1}', r#"\rightharpoondown"#);
390    map.insert('\u{21c2}', r#"\downharpoonright"#);
391    map.insert('\u{21c3}', r#"\downharpoonleft"#);
392    map.insert('\u{21c4}', r#"\rightleftarrows"#);
393    map.insert('\u{21c5}', r#"\updownarrows"#);
394    map.insert('\u{21c6}', r#"\leftrightarrows"#);
395    map.insert('\u{21c7}', r#"\leftleftarrows"#);
396    map.insert('\u{21c8}', r#"\upuparrows"#);
397    map.insert('\u{21c9}', r#"\rightrightarrows"#);
398    map.insert('\u{21ca}', r#"\downdownarrows"#);
399    map.insert('\u{21cb}', r#"\leftrightharpoons"#);
400    map.insert('\u{21cc}', r#"\rightleftharpoons"#);
401    map.insert('\u{21cd}', r#"\nLeftarrow"#);
402    map.insert('\u{21ce}', r#"\nLeftrightarrow"#);
403    map.insert('\u{21cf}', r#"\nRightarrow"#);
404    map.insert('\u{21cf}', r#"\nRightarrow"#);
405    map.insert('\u{21d0}', r#"\Leftarrow"#);
406    map.insert('\u{21d1}', r#"\Uparrow"#);
407    map.insert('\u{21d2}', r#"\Rightarrow"#);
408    map.insert('\u{21d3}', r#"\Downarrow"#);
409    map.insert('\u{21d4}', r#"\Leftrightarrow"#);
410    map.insert('\u{21d5}', r#"\Updownarrowarrow"#);
411    map.insert('\u{21d6}', r#"\Nwarrow"#);
412    map.insert('\u{21d7}', r#"\Searrow"#);
413    map.insert('\u{21d8}', r#"\Swarrow"#);
414    map.insert('\u{21da}', r#"\Lleftarrow"#);
415    map.insert('\u{21db}', r#"\Rrightarrow"#);
416    map.insert('\u{21dc}', r#"\leftsquigarrow"#);
417    map.insert('\u{21dd}', r#"\rightsquigarrow"#);
418    //
419    map.insert('\u{21f5}', r#"\downuparrows"#);
420
421
422    // Mathematical operators
423    map.insert('\u{2200}', r#"\forall"#);
424    map.insert('\u{2201}', r#"\complement"#);
425    map.insert('\u{2202}', r#"\partial"#);
426    map.insert('\u{2203}', r#"\exists"#);
427    map.insert('\u{2204}', r#"\nexists"#);
428    map.insert('\u{2205}', r#"\varnothing"#);
429    map.insert('\u{2206}', r#"\Delta"#); // \mathrm{\Delta}
430    map.insert('\u{2207}', r#"\nabla"#);
431    map.insert('\u{2208}', r#"\in"#);
432    map.insert('\u{2209}', r#"\notin"#);
433    map.insert('\u{220a}', r#"\epsilon"#);
434    map.insert('\u{220b}', r#"\ni"#);
435    map.insert('\u{220c}', r#"\nni"#);
436    //
437    map.insert('\u{220f}', r#"\prod"#);
438    map.insert('\u{2210}', r#"\coprod"#);
439    map.insert('\u{2211}', r#"\sum"#);
440    map.insert('\u{2212}', r#"-"#);
441    map.insert('\u{2213}', r#"\mp"#);
442    map.insert('\u{2214}', r#"\dotplus"#);
443    map.insert('\u{2215}', r#"\slash"#);
444    map.insert('\u{2216}', r#"\smallsetminus"#);
445    map.insert('\u{2217}', r#"\ast"#);
446    map.insert('\u{2218}', r#"\circ"#);
447    map.insert('\u{2219}', r#"\bullet"#);
448    map.insert('\u{221a}', r#"\sqrt"#);
449    map.insert('\u{221b}', r#"\sqrt[3]"#);
450    map.insert('\u{221c}', r#"\sqrt[4]"#);
451    map.insert('\u{221d}', r#"\propto"#);
452    map.insert('\u{221e}', r#"\infty"#);
453    //
454    map.insert('\u{2227}', r#"\wedge"#);
455    map.insert('\u{2228}', r#"\vee"#);
456    map.insert('\u{2229}', r#"\cap"#);
457    map.insert('\u{222a}', r#"\cup"#);
458    map.insert('\u{222b}', r#"\int"#);
459    map.insert('\u{222c}', r#"\iint"#);
460    map.insert('\u{222d}', r#"\iiint"#);
461    map.insert('\u{222e}', r#"\oint"#);
462    map.insert('\u{222f}', r#"\oiint"#);
463    map.insert('\u{2230}', r#"\oiiint"#);
464    //
465    map.insert('\u{2234}', r#"\therefore"#);
466    map.insert('\u{2235}', r#"\because"#);
467    map.insert('\u{2236}', r#":"#);
468    map.insert('\u{2237}', r#"\Proportion"#);
469    //
470    map.insert('\u{223c}', r#"\sim"#);
471    map.insert('\u{223d}', r#"\backsim"#);
472    //
473    map.insert('\u{2241}', r#"\nsim"#);
474    //
475    map.insert('\u{2248}', r#"\approx"#);
476    map.insert('\u{2249}', r#"\napprox"#);
477    //
478    map.insert('\u{2254}', r#"\coloneq"#);
479    map.insert('\u{2255}', r#"\eqcolon"#);
480    //
481    map.insert('\u{2259}', r#"\corresponds"#);
482    //
483    map.insert('\u{2260}', r#"\neq"#);
484    map.insert('\u{2261}', r#"\equiv"#);
485    map.insert('\u{2262}', r#"\nequiv"#);
486    //
487    map.insert('\u{2264}', r#"\leq"#);
488    map.insert('\u{2265}', r#"\geq"#);
489    //
490    map.insert('\u{2264}', r#"\ll"#);
491    map.insert('\u{2264}', r#"\gg"#);
492    //
493    map.insert('\u{226e}', r#"\nless"#);
494    map.insert('\u{226f}', r#"\ngtr"#);
495    map.insert('\u{2270}', r#"\nleq"#);
496    map.insert('\u{2271}', r#"\ngeq"#);
497    //
498    map.insert('\u{227a}', r#"\prec"#);
499    map.insert('\u{227b}', r#"\succ"#);
500    map.insert('\u{2270}', r#"\preccurlyeq"#);
501    map.insert('\u{2270}', r#"\succurlyeq"#);
502    map.insert('\u{2270}', r#"\precsim"#);
503    map.insert('\u{2270}', r#"\succsim"#);
504    map.insert('\u{2270}', r#"\nprec"#);
505    map.insert('\u{2270}', r#"\nsucc"#);
506
507    map.insert('\u{2282}', r#"\subset"#);
508    map.insert('\u{2283}', r#"\supset"#);
509    map.insert('\u{2284}', r#"\nsubset"#);
510    map.insert('\u{2285}', r#"\nsupset"#);
511    map.insert('\u{2286}', r#"\subseteq"#);
512    map.insert('\u{2287}', r#"\supseteq"#);
513    map.insert('\u{2288}', r#"\nsubseteq"#);
514    map.insert('\u{2289}', r#"\nsupseteq"#);
515    map.insert('\u{228a}', r#"\subsetneq"#);
516    map.insert('\u{228b}', r#"\supsetneq"#);
517    //
518    map.insert('\u{22a2}', r#"\vdash"#);
519    map.insert('\u{22a3}', r#"\dashv"#);
520    map.insert('\u{22a4}', r#"\top"#);
521    map.insert('\u{22a5}', r#"\bot"#);
522    //
523    map.insert('\u{22a7}', r#"\models"#);
524    map.insert('\u{22a8}', r#"\vDash"#);
525    //
526    map.insert('\u{22c0}', r#"\bigwedge"#);
527    map.insert('\u{22c1}', r#"\bigvee"#);
528    map.insert('\u{22c2}', r#"\bigcap"#);
529    map.insert('\u{22c3}', r#"\bigcup"#);
530    map.insert('\u{22c4}', r#"\diamond"#);
531    map.insert('\u{22c5}', r#"\cdot"#);
532    map.insert('\u{22c6}', r#"\star"#);
533    //
534    map.insert('\u{22ce}', r#"\curlyvee"#);
535    map.insert('\u{22cf}', r#"\curlywedge"#);
536
537    map
538  };
539
540  /// A mapping of characters that LaTeX does not recognize as text to escaped versions of them.
541  /// This allows the parser to transform any plain text node contents into LaTeX-compatible strings.
542  static ref UTF8_TEXT_TO_LATEX_MAP: HashMap<char, &'static str> = {
543
544    let mut map = HashMap::new();
545
546    map.insert('\\', r#"\textbackslash"#);
547    map.insert('^', r#"\textasciicircum"#);
548    map.insert('_', r#"\_"#);
549    map.insert('~', r#"\~"#);
550    map.insert('$', r#"\$"#);
551    map.insert('{', r#"\{"#);
552    map.insert('}', r#"\}"#);
553    map.insert('#', r#"\#"#);
554    map.insert('&', r#"\&"#);
555    map.insert('%', r#"\%"#);
556
557    map
558  };
559}