use lazy_static::lazy_static;
use std::collections::HashMap;
lazy_static! {
pub static ref MISC_SYMBOLS: HashMap<&'static str, &'static str> = {
let mut m = HashMap::new();
m.insert("\\pounds", "£");
m.insert("\\euro", "€");
m.insert("\\yen", "¥");
m.insert("\\copyright", "©");
m.insert("\\registered", "®");
m.insert("\\trademark", "™");
m.insert("\\textregistered", "®");
m.insert("\\textcopyright", "©");
m.insert("\\texttrademark", "™");
m.insert("\\textasciicircum", "^");
m.insert("\\textasciitilde", "~");
m.insert("\\textbaht", "฿");
m.insert("\\textblank", "␢");
m.insert("\\textbigcircle", "○");
m.insert("\\textbrokenbar", "¦");
m.insert("\\textbullet", "•");
m.insert("\\textcentoldstyle", "¢");
m.insert("\\textcent", "¢");
m.insert("\\textdagger", "†");
m.insert("\\textdaggerdbl", "‡");
m.insert("\\textdegree", "°");
m.insert("\\textdollar", "\\$"); m.insert("\\textdong", "₫");
m.insert("\\textlira", "₤");
m.insert("\\textmu", "μ");
m.insert("\\textmusicalnote", "♪");
m.insert("\\textonehalf", "½");
m.insert("\\textonequarter", "¼");
m.insert("\\textthreequarters", "¾");
m.insert("\\textparagraph", "¶");
m.insert("\\textpertenthousand", "‱");
m.insert("\\textperthousand", "‰");
m.insert("\\textpeso", "₱");
m.insert("\\textquotesingle", "'");
m.insert("\\textsection", "§");
m.insert("\\textsterling", "£");
m.insert("\\textthreesuperior", "³");
m.insert("\\texttwosuperior", "²");
m.insert("\\textonesuperior", "¹");
m.insert("\\textyen", "¥");
m.insert("\\textordfeminine", "ª");
m.insert("\\textordmasculine", "º");
m.insert("\\texteuro", "€");
m.insert("\\textellipsis", "…");
m.insert("\\textendash", "–");
m.insert("\\textemdash", "—");
m.insert("\\textexclamdown", "¡");
m.insert("\\textquestiondown", "¿");
m.insert("\\textleftarrow", "←");
m.insert("\\textrightarrow", "→");
m.insert("\\textuparrow", "↑");
m.insert("\\textdownarrow", "↓");
m.insert("\\textquoteleft", "\u{2018}"); m.insert("\\textquoteright", "\u{2019}"); m.insert("\\textquotedblleft", "\u{201C}"); m.insert("\\textquotedblright", "\u{201D}"); m.insert("\\guillemotleft", "\u{00AB}"); m.insert("\\guillemotright", "\u{00BB}"); m.insert("\\guilsinglleft", "\u{2039}"); m.insert("\\guilsinglright", "\u{203A}"); m.insert("\\quotedblbase", "\u{201E}"); m.insert("\\quotesinglbase", "\u{201A}");
m.insert("\\textspade", "♠");
m.insert("\\textheart", "♥");
m.insert("\\textdiamond", "♦");
m.insert("\\textclub", "♣");
m.insert("\\checkmark", "✓");
m.insert("\\textcheckmark", "✓");
m.insert("\\textcross", "✗");
m.insert("\\textinterrobang", "‽");
m.insert("\\textreferencemark", "※");
m.insert("\\textdied", "†");
m.insert("\\textborn", "∗");
m.insert("\\textmarried", "⚭");
m.insert("\\textdivorced", "⚮");
m
};
pub static ref CHAR_COMMANDS: HashMap<&'static str, &'static str> = {
let mut m = HashMap::new();
m.insert("\\ldots", "…");
m.insert("\\vdots", "⋮");
m.insert("\\cdots", "⋯");
m.insert("\\ddots", "⋱");
m.insert("\\dots", "…");
m.insert("\\mdots", "…");
m.insert("\\textellipsis", "…");
m.insert("\\sim", "~");
m.insert("\\sep", ",");
m.insert("\\P", "¶");
m.insert("\\S", "§");
m.insert("\\$", "\\$"); m.insert("\\%", "%"); m.insert("\\&", "&"); m.insert("\\#", "\\#"); m.insert("\\_", "\\_"); m.insert("\\{", "{");
m.insert("\\}", "}");
m.insert("\\-", "\u{00ad}"); m.insert("\\qed", "∎");
m.insert("\\lq", "'");
m.insert("\\rq", "'");
m.insert("\\/", ""); m.insert("\\,", "\u{2006}"); m.insert("\\;", "\u{2009}"); m.insert("\\:", "\u{2005}"); m.insert("\\!", ""); m.insert("\\@", "");
m.insert("\\ ", "\u{00a0}"); m.insert("\\~", "\u{00a0}"); m.insert("\\ps", "PS.");
m.insert("\\TeX", "TeX");
m.insert("\\LaTeX", "LaTeX");
m.insert("\\LaTeXe", "LaTeX2ε");
m.insert("\\XeTeX", "XeTeX");
m.insert("\\XeLaTeX", "XeLaTeX");
m.insert("\\LuaTeX", "LuaTeX");
m.insert("\\LuaLaTeX", "LuaLaTeX");
m.insert("\\pdfTeX", "pdfTeX");
m.insert("\\pdfLaTeX", "pdfLaTeX");
m.insert("\\BibTeX", "BibTeX");
m.insert("\\bar", "|");
m.insert("\\textless", "<");
m.insert("\\textgreater", ">");
m.insert("\\textbackslash", "\\");
m.insert("\\backslash", "\\");
m.insert("\\slash", "/");
m.insert("\\textbar", "|");
m.insert("\\textbraceleft", "{");
m.insert("\\textbraceright", "}");
m.insert("\\textunderscore", "_");
m.insert("\\textvisiblespace", "␣");
m.insert("\\faCheck", "✓");
m.insert("\\faClose", "✗");
m.insert("\\faTimes", "✗");
m.insert("\\faPlus", "+");
m.insert("\\faMinus", "−");
m.insert("\\faSearch", "🔍");
m.insert("\\faHome", "🏠");
m.insert("\\faUser", "👤");
m.insert("\\faEnvelope", "✉");
m.insert("\\faPhone", "📞");
m.insert("\\faStar", "★");
m.insert("\\faHeart", "♥");
m.insert("\\faThumbsUp", "👍");
m.insert("\\faThumbsDown", "👎");
m.insert("\\faWarning", "⚠");
m.insert("\\faInfo", "ℹ");
m.insert("\\faQuestion", "?");
m.insert("\\faExclamation", "!");
m.insert("\\faArrowRight", "→");
m.insert("\\faArrowLeft", "←");
m.insert("\\faArrowUp", "↑");
m.insert("\\faArrowDown", "↓");
m.insert("\\bshyp", "\\\u{00ad}");
m.insert("\\fshyp", "/\u{00ad}");
m.insert("\\dothyp", ".\u{00ad}");
m.insert("\\colonhyp", ":\u{00ad}");
m.insert("\\hyp", "-");
m
};
pub static ref ACCENT_COMMANDS: HashMap<&'static str, char> = {
let mut m = HashMap::new();
m.insert("\\`", '\u{0300}'); m.insert("\\'", '\u{0301}'); m.insert("\\^", '\u{0302}'); m.insert("\\~", '\u{0303}'); m.insert("\\\"", '\u{0308}'); m.insert("\\=", '\u{0304}'); m.insert("\\.", '\u{0307}'); m.insert("\\u", '\u{0306}'); m.insert("\\v", '\u{030C}'); m.insert("\\H", '\u{030B}'); m.insert("\\c", '\u{0327}'); m.insert("\\k", '\u{0328}'); m.insert("\\d", '\u{0323}'); m.insert("\\b", '\u{0331}'); m.insert("\\t", '\u{0361}'); m.insert("\\r", '\u{030A}'); m.insert("\\h", '\u{0309}'); m.insert("\\G", '\u{030F}'); m.insert("\\f", '\u{0311}'); m.insert("\\U", '\u{030E}'); m.insert("\\textogonekcentered", '\u{0328}');
m
};
pub static ref LETTER_COMMANDS: HashMap<&'static str, &'static str> = {
let mut m = HashMap::new();
m.insert("\\aa", "å");
m.insert("\\AA", "Å");
m.insert("\\ae", "æ");
m.insert("\\AE", "Æ");
m.insert("\\oe", "œ");
m.insert("\\OE", "Œ");
m.insert("\\o", "ø");
m.insert("\\O", "Ø");
m.insert("\\l", "ł");
m.insert("\\L", "Ł");
m.insert("\\ss", "ß");
m.insert("\\SS", "ẞ");
m.insert("\\i", "ı"); m.insert("\\j", "ȷ"); m.insert("\\dh", "ð");
m.insert("\\DH", "Ð");
m.insert("\\th", "þ");
m.insert("\\TH", "Þ");
m.insert("\\dj", "đ");
m.insert("\\DJ", "Đ");
m.insert("\\ng", "ŋ");
m.insert("\\NG", "Ŋ");
m.insert("\\ij", "ij");
m.insert("\\IJ", "IJ");
m
};
pub static ref BIBLATEX_COMMANDS: HashMap<&'static str, &'static str> = {
let mut m = HashMap::new();
m.insert("\\mkbibquote", "\""); m.insert("\\mkbibemph", "_"); m.insert("\\mkbibitalic", "_"); m.insert("\\mkbibbold", "*"); m.insert("\\autocap", ""); m.insert("\\textnormal", ""); m.insert("\\adddot", ".");
m.insert("\\adddotspace", ". ");
m.insert("\\addabbrvspace", " ");
m.insert("\\addcomma", ",");
m.insert("\\addcolon", ":");
m.insert("\\addsemicolon", ";");
m.insert("\\addperiod", ".");
m.insert("\\addspace", " ");
m.insert("\\hyphen", "-");
m.insert("\\textendash", "–");
m.insert("\\textemdash", "—");
m
};
pub static ref NAME_COMMANDS: HashMap<&'static str, &'static str> = {
let mut m = HashMap::new();
m.insert("\\figurename", "Figure");
m.insert("\\tablename", "Table");
m.insert("\\prefacename", "Preface");
m.insert("\\refname", "References");
m.insert("\\bibname", "Bibliography");
m.insert("\\chaptername", "Chapter");
m.insert("\\partname", "Part");
m.insert("\\contentsname", "Contents");
m.insert("\\listfigurename", "List of Figures");
m.insert("\\listtablename", "List of Tables");
m.insert("\\indexname", "Index");
m.insert("\\abstractname", "Abstract");
m.insert("\\enclname", "Enclosure");
m.insert("\\ccname", "CC");
m.insert("\\headtoname", "To");
m.insert("\\pagename", "Page");
m.insert("\\seename", "see");
m.insert("\\seealsoname", "see also");
m.insert("\\proofname", "Proof");
m.insert("\\glossaryname", "Glossary");
m.insert("\\lstlistingname", "Listing");
m.insert("\\appendixname", "Appendix");
m.insert("\\acknowledgementname", "Acknowledgement");
m.insert("\\algorithname", "Algorithm");
m.insert("\\assumptionname", "Assumption");
m.insert("\\axiomname", "Axiom");
m.insert("\\casename", "Case");
m.insert("\\claimname", "Claim");
m.insert("\\conclusionname", "Conclusion");
m.insert("\\conditionname", "Condition");
m.insert("\\conjecturename", "Conjecture");
m.insert("\\corollaryname", "Corollary");
m.insert("\\criterionname", "Criterion");
m.insert("\\definitionname", "Definition");
m.insert("\\examplename", "Example");
m.insert("\\exercisename", "Exercise");
m.insert("\\hypothesisname", "Hypothesis");
m.insert("\\lemmaname", "Lemma");
m.insert("\\notationname", "Notation");
m.insert("\\problemname", "Problem");
m.insert("\\propertyname", "Property");
m.insert("\\propositionname", "Proposition");
m.insert("\\questionname", "Question");
m.insert("\\remarkname", "Remark");
m.insert("\\solutionname", "Solution");
m.insert("\\summaryname", "Summary");
m.insert("\\theoremname", "Theorem");
m
};
pub static ref GREEK_LETTERS: HashMap<&'static str, &'static str> = {
let mut m = HashMap::new();
m.insert("\\alpha", "α");
m.insert("\\beta", "β");
m.insert("\\gamma", "γ");
m.insert("\\delta", "δ");
m.insert("\\epsilon", "ε");
m.insert("\\varepsilon", "ε");
m.insert("\\zeta", "ζ");
m.insert("\\eta", "η");
m.insert("\\theta", "θ");
m.insert("\\vartheta", "ϑ");
m.insert("\\iota", "ι");
m.insert("\\kappa", "κ");
m.insert("\\lambda", "λ");
m.insert("\\mu", "μ");
m.insert("\\nu", "ν");
m.insert("\\xi", "ξ");
m.insert("\\pi", "π");
m.insert("\\varpi", "ϖ");
m.insert("\\rho", "ρ");
m.insert("\\varrho", "ϱ");
m.insert("\\sigma", "σ");
m.insert("\\varsigma", "ς");
m.insert("\\tau", "τ");
m.insert("\\upsilon", "υ");
m.insert("\\phi", "φ");
m.insert("\\varphi", "φ");
m.insert("\\chi", "χ");
m.insert("\\psi", "ψ");
m.insert("\\omega", "ω");
m.insert("\\Alpha", "Α");
m.insert("\\Beta", "Β");
m.insert("\\Gamma", "Γ");
m.insert("\\Delta", "Δ");
m.insert("\\Epsilon", "Ε");
m.insert("\\Zeta", "Ζ");
m.insert("\\Eta", "Η");
m.insert("\\Theta", "Θ");
m.insert("\\Iota", "Ι");
m.insert("\\Kappa", "Κ");
m.insert("\\Lambda", "Λ");
m.insert("\\Mu", "Μ");
m.insert("\\Nu", "Ν");
m.insert("\\Xi", "Ξ");
m.insert("\\Pi", "Π");
m.insert("\\Rho", "Ρ");
m.insert("\\Sigma", "Σ");
m.insert("\\Tau", "Τ");
m.insert("\\Upsilon", "Υ");
m.insert("\\Phi", "Φ");
m.insert("\\Chi", "Χ");
m.insert("\\Psi", "Ψ");
m.insert("\\Omega", "Ω");
m
};
pub static ref TEXT_FORMAT_COMMANDS: HashMap<&'static str, (&'static str, &'static str)> = {
let mut m = HashMap::new();
m.insert("\\textbf", ("*", "*"));
m.insert("\\textit", ("_", "_"));
m.insert("\\emph", ("_", "_"));
m.insert("\\texttt", ("`", "`"));
m.insert("\\textsc", ("#smallcaps[", "]"));
m.insert("\\textsf", ("", "")); m.insert("\\textrm", ("", "")); m.insert("\\textup", ("", "")); m.insert("\\textsl", ("_", "_")); m.insert("\\underline", ("#underline[", "]"));
m.insert("\\uline", ("#underline[", "]")); m.insert("\\sout", ("#strike[", "]")); m.insert("\\st", ("#strike[", "]")); m.insert("\\hl", ("#highlight[", "]")); m.insert("\\textsubscript", ("#sub[", "]"));
m.insert("\\textsuperscript", ("#super[", "]"));
m.insert("\\enquote", ("\"", "\"")); m
};
}
fn replace_command_safe(input: &str, cmd: &str, replacement: &str) -> String {
let mut result = String::new();
let mut remaining = input;
while let Some(pos) = remaining.find(cmd) {
result.push_str(&remaining[..pos]);
let after_cmd = &remaining[pos + cmd.len()..];
let next_char = after_cmd.chars().next();
if next_char.map(|c| c.is_ascii_alphabetic()).unwrap_or(false) {
result.push_str(cmd);
} else {
result.push_str(replacement);
}
remaining = after_cmd;
}
result.push_str(remaining);
result
}
pub fn apply_symbol_replacements(input: &str) -> String {
let mut result = input.to_string();
for (latex, typst) in MISC_SYMBOLS.iter() {
result = result.replace(latex, typst);
}
for (latex, typst) in CHAR_COMMANDS.iter() {
result = result.replace(latex, typst);
}
for (latex, typst) in LETTER_COMMANDS.iter() {
result = replace_command_safe(&result, latex, typst);
}
for (latex, typst) in BIBLATEX_COMMANDS.iter() {
result = result.replace(latex, typst);
}
for (latex, typst) in NAME_COMMANDS.iter() {
result = result.replace(latex, typst);
}
for (latex, typst) in GREEK_LETTERS.iter() {
result = result.replace(latex, typst);
}
result
}
pub fn apply_accent(base: char, accent_cmd: &str) -> Option<String> {
if let Some(&combining) = ACCENT_COMMANDS.get(accent_cmd) {
let mut result = String::new();
result.push(base);
result.push(combining);
Some(unicode_normalization_nfc(&result))
} else {
None
}
}
fn unicode_normalization_nfc(s: &str) -> String {
let common_compositions: HashMap<&str, &str> = [
("à", "à"),
("á", "á"),
("â", "â"),
("ã", "ã"),
("ä", "ä"),
("å", "å"),
("è", "è"),
("é", "é"),
("ê", "ê"),
("ë", "ë"),
("ì", "ì"),
("í", "í"),
("î", "î"),
("ï", "ï"),
("ò", "ò"),
("ó", "ó"),
("ô", "ô"),
("õ", "õ"),
("ö", "ö"),
("ù", "ù"),
("ú", "ú"),
("û", "û"),
("ü", "ü"),
("ñ", "ñ"),
("ç", "ç"),
("À", "À"),
("Á", "Á"),
("Â", "Â"),
("Ã", "Ã"),
("Ä", "Ä"),
("Å", "Å"),
("È", "È"),
("É", "É"),
("Ê", "Ê"),
("Ë", "Ë"),
("Ì", "Ì"),
("Í", "Í"),
("Î", "Î"),
("Ï", "Ï"),
("Ò", "Ò"),
("Ó", "Ó"),
("Ô", "Ô"),
("Õ", "Õ"),
("Ö", "Ö"),
("Ù", "Ù"),
("Ú", "Ú"),
("Û", "Û"),
("Ü", "Ü"),
("Ñ", "Ñ"),
("Ç", "Ç"),
]
.iter()
.cloned()
.collect();
let mut result = s.to_string();
for (decomposed, composed) in common_compositions.iter() {
result = result.replace(decomposed, composed);
}
result
}
pub fn convert_text_format(cmd: &str, content: &str) -> Option<String> {
if let Some((prefix, suffix)) = TEXT_FORMAT_COMMANDS.get(cmd) {
Some(format!("{}{}{}", prefix, content, suffix))
} else {
None
}
}
pub fn process_accent_commands(input: &str) -> String {
let mut result = input.to_string();
let accent_map: &[(&str, &str)] = &[
("\\'a", "á"),
("\\'e", "é"),
("\\'i", "í"),
("\\'o", "ó"),
("\\'u", "ú"),
("\\'A", "Á"),
("\\'E", "É"),
("\\'I", "Í"),
("\\'O", "Ó"),
("\\'U", "Ú"),
("\\'y", "ý"),
("\\'Y", "Ý"),
("\\'c", "ć"),
("\\'C", "Ć"),
("\\'n", "ń"),
("\\'N", "Ń"),
("\\'s", "ś"),
("\\'S", "Ś"),
("\\'z", "ź"),
("\\'Z", "Ź"),
("\\'r", "ŕ"),
("\\'R", "Ŕ"),
("\\'l", "ĺ"),
("\\'L", "Ĺ"),
("\\'{a}", "á"),
("\\'{e}", "é"),
("\\'{i}", "í"),
("\\'{o}", "ó"),
("\\'{u}", "ú"),
("\\'{A}", "Á"),
("\\'{E}", "É"),
("\\'{I}", "Í"),
("\\'{O}", "Ó"),
("\\'{U}", "Ú"),
("\\`a", "à"),
("\\`e", "è"),
("\\`i", "ì"),
("\\`o", "ò"),
("\\`u", "ù"),
("\\`A", "À"),
("\\`E", "È"),
("\\`I", "Ì"),
("\\`O", "Ò"),
("\\`U", "Ù"),
("\\`{a}", "à"),
("\\`{e}", "è"),
("\\`{i}", "ì"),
("\\`{o}", "ò"),
("\\`{u}", "ù"),
("\\^a", "â"),
("\\^e", "ê"),
("\\^i", "î"),
("\\^o", "ô"),
("\\^u", "û"),
("\\^A", "Â"),
("\\^E", "Ê"),
("\\^I", "Î"),
("\\^O", "Ô"),
("\\^U", "Û"),
("\\^{a}", "â"),
("\\^{e}", "ê"),
("\\^{i}", "î"),
("\\^{o}", "ô"),
("\\^{u}", "û"),
("\\~a", "ã"),
("\\~o", "õ"),
("\\~n", "ñ"),
("\\~A", "Ã"),
("\\~O", "Õ"),
("\\~N", "Ñ"),
("\\~{a}", "ã"),
("\\~{o}", "õ"),
("\\~{n}", "ñ"),
("\\\"a", "ä"),
("\\\"e", "ë"),
("\\\"i", "ï"),
("\\\"o", "ö"),
("\\\"u", "ü"),
("\\\"A", "Ä"),
("\\\"E", "Ë"),
("\\\"I", "Ï"),
("\\\"O", "Ö"),
("\\\"U", "Ü"),
("\\\"y", "ÿ"),
("\\\"Y", "Ÿ"),
("\\\"{a}", "ä"),
("\\\"{e}", "ë"),
("\\\"{i}", "ï"),
("\\\"{o}", "ö"),
("\\\"{u}", "ü"),
("\\c{c}", "ç"),
("\\c{C}", "Ç"),
("\\c c", "ç"),
("\\c C", "Ç"),
("\\c{s}", "ş"),
("\\c{S}", "Ş"),
("\\v{c}", "č"),
("\\v{C}", "Č"),
("\\v{s}", "š"),
("\\v{S}", "Š"),
("\\v{z}", "ž"),
("\\v{Z}", "Ž"),
("\\v{e}", "ě"),
("\\v{E}", "Ě"),
("\\v{r}", "ř"),
("\\v{R}", "Ř"),
("\\v{n}", "ň"),
("\\v{N}", "Ň"),
("\\v{d}", "ď"),
("\\v{D}", "Ď"),
("\\v{t}", "ť"),
("\\v{T}", "Ť"),
("\\=a", "ā"),
("\\=e", "ē"),
("\\=i", "ī"),
("\\=o", "ō"),
("\\=u", "ū"),
("\\=A", "Ā"),
("\\=E", "Ē"),
("\\=I", "Ī"),
("\\=O", "Ō"),
("\\=U", "Ū"),
("\\={a}", "ā"),
("\\={e}", "ē"),
("\\={i}", "ī"),
("\\={o}", "ō"),
("\\={u}", "ū"),
("\\u{a}", "ă"),
("\\u{A}", "Ă"),
("\\u{g}", "ğ"),
("\\u{G}", "Ğ"),
("\\u{i}", "ĭ"),
("\\u{I}", "Ĭ"),
("\\.{z}", "ż"),
("\\.{Z}", "Ż"),
("\\.{e}", "ė"),
("\\.{E}", "Ė"),
("\\.{c}", "ċ"),
("\\.{C}", "Ċ"),
("\\.{g}", "ġ"),
("\\.{G}", "Ġ"),
("\\.{I}", "İ"),
("\\r{a}", "å"),
("\\r{A}", "Å"),
("\\r{u}", "ů"),
("\\r{U}", "Ů"),
("\\k{a}", "ą"),
("\\k{A}", "Ą"),
("\\k{e}", "ę"),
("\\k{E}", "Ę"),
("\\H{o}", "ő"),
("\\H{O}", "Ő"),
("\\H{u}", "ű"),
("\\H{U}", "Ű"),
];
for (latex, unicode) in accent_map {
result = result.replace(latex, unicode);
}
result
}
lazy_static! {
pub static ref BIG_DELIMITER_COMMANDS: std::collections::HashSet<&'static str> = {
let mut s = std::collections::HashSet::new();
s.insert("big");
s.insert("Big");
s.insert("bigg");
s.insert("Bigg");
s.insert("bigl");
s.insert("bigr");
s.insert("Bigl");
s.insert("Bigr");
s.insert("biggl");
s.insert("biggr");
s.insert("Biggl");
s.insert("Biggr");
s.insert("bigm");
s.insert("Bigm");
s.insert("biggm");
s.insert("Biggm");
s
};
pub static ref DELIMITER_TO_TYPST: HashMap<&'static str, &'static str> = {
let mut m = HashMap::new();
m.insert("[", "[");
m.insert("]", "]");
m.insert("(", "(");
m.insert(")", ")");
m.insert("\\lbrack", "[");
m.insert("\\rbrack", "]");
m.insert("\\lparen", "(");
m.insert("\\rparen", ")");
m.insert("\\{", "{");
m.insert("\\}", "}");
m.insert("\\lbrace", "{");
m.insert("\\rbrace", "}");
m.insert("|", "bar.v");
m.insert("\\vert", "bar.v");
m.insert("\\lvert", "bar.v");
m.insert("\\rvert", "bar.v");
m.insert("\\|", "bar.v.double");
m.insert("\\Vert", "bar.v.double");
m.insert("\\lVert", "bar.v.double");
m.insert("\\rVert", "bar.v.double");
m.insert("\\langle", "chevron.l");
m.insert("\\rangle", "chevron.r");
m.insert("\\lfloor", "floor.l");
m.insert("\\rfloor", "floor.r");
m.insert("\\lceil", "ceil.l");
m.insert("\\rceil", "ceil.r");
m.insert(".", "");
m
};
}
pub fn is_big_delimiter_command(cmd: &str) -> bool {
BIG_DELIMITER_COMMANDS.contains(cmd)
}
pub fn convert_delimiter(delim: &str) -> Option<&'static str> {
DELIMITER_TO_TYPST.get(delim).copied()
}
lazy_static! {
pub static ref CAPTION_TEXT_COMMANDS: std::collections::HashSet<&'static str> = {
let mut s = std::collections::HashSet::new();
s.insert("textbf");
s.insert("textit");
s.insert("texttt");
s.insert("textrm");
s.insert("textsc");
s.insert("textsf");
s.insert("emph");
s.insert("underline");
s.insert("text");
s.insert("mbox");
s.insert("hbox");
s
};
}
pub fn is_caption_text_command(cmd: &str) -> bool {
CAPTION_TEXT_COMMANDS.contains(cmd)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_symbol_replacements() {
assert!(apply_symbol_replacements("\\pounds").contains("£"));
assert!(apply_symbol_replacements("\\euro").contains("€"));
assert!(apply_symbol_replacements("\\ldots").contains("…"));
}
#[test]
fn test_accent_processing() {
assert_eq!(process_accent_commands("\\'e"), "é");
assert_eq!(process_accent_commands("\\\"o"), "ö");
assert_eq!(process_accent_commands("\\~n"), "ñ");
assert_eq!(process_accent_commands("\\c{c}"), "ç");
}
#[test]
fn test_text_format() {
assert_eq!(
convert_text_format("\\textbf", "bold"),
Some("*bold*".to_string())
);
assert_eq!(
convert_text_format("\\emph", "italic"),
Some("_italic_".to_string())
);
assert_eq!(
convert_text_format("\\texttt", "code"),
Some("`code`".to_string())
);
}
#[test]
fn test_greek_letters() {
let result = apply_symbol_replacements("\\alpha \\beta \\gamma");
assert!(result.contains("α"));
assert!(result.contains("β"));
assert!(result.contains("γ"));
}
}