use rust_latex_parser::MathFontKind;
pub fn map_char(kind: &MathFontKind, ch: char) -> char {
match kind {
MathFontKind::Bold => to_bold(ch),
MathFontKind::Blackboard => to_double_struck(ch),
MathFontKind::Calligraphic => to_script(ch),
MathFontKind::Fraktur => to_fraktur(ch),
MathFontKind::Roman => ch, MathFontKind::SansSerif => to_sans_serif(ch),
MathFontKind::Monospace => to_monospace(ch),
}
}
pub fn map_str(kind: &MathFontKind, s: &str) -> String {
s.chars().map(|c| map_char(kind, c)).collect()
}
fn to_bold(ch: char) -> char {
match ch {
'A'..='Z' => char::from_u32(0x1D400 + (ch as u32 - 'A' as u32)).unwrap_or(ch),
'a'..='z' => char::from_u32(0x1D41A + (ch as u32 - 'a' as u32)).unwrap_or(ch),
'0'..='9' => char::from_u32(0x1D7CE + (ch as u32 - '0' as u32)).unwrap_or(ch),
'Α'..='Ω' => char::from_u32(0x1D6A8 + (ch as u32 - 'Α' as u32)).unwrap_or(ch),
'α'..='ω' => char::from_u32(0x1D6C2 + (ch as u32 - 'α' as u32)).unwrap_or(ch),
_ => ch,
}
}
fn to_double_struck(ch: char) -> char {
match ch {
'C' => 'ℂ',
'H' => 'ℍ',
'N' => 'ℕ',
'P' => 'ℙ',
'Q' => 'ℚ',
'R' => 'ℝ',
'Z' => 'ℤ',
'A' | 'B' | 'D'..='G' | 'I'..='M' | 'O' | 'S'..='Y' => {
char::from_u32(0x1D538 + (ch as u32 - 'A' as u32)).unwrap_or(ch)
}
'a'..='z' => char::from_u32(0x1D552 + (ch as u32 - 'a' as u32)).unwrap_or(ch),
'0'..='9' => char::from_u32(0x1D7D8 + (ch as u32 - '0' as u32)).unwrap_or(ch),
_ => ch,
}
}
fn to_script(ch: char) -> char {
match ch {
'B' => 'ℬ',
'E' => 'ℰ',
'F' => 'ℱ',
'H' => 'ℋ',
'I' => 'ℐ',
'L' => 'ℒ',
'M' => 'ℳ',
'R' => 'ℛ',
'e' => 'ℯ',
'g' => 'ℊ',
'o' => 'ℴ',
'A' | 'C' | 'D' | 'G' | 'J' | 'K' | 'N'..='Q' | 'S'..='Z' => {
char::from_u32(0x1D49C + (ch as u32 - 'A' as u32)).unwrap_or(ch)
}
'a'..='d' | 'f' | 'h'..='n' | 'p'..='z' => {
char::from_u32(0x1D4B6 + (ch as u32 - 'a' as u32)).unwrap_or(ch)
}
_ => ch,
}
}
fn to_fraktur(ch: char) -> char {
match ch {
'C' => 'ℭ',
'H' => 'ℌ',
'I' => 'ℑ',
'R' => 'ℜ',
'Z' => 'ℨ',
'A' | 'B' | 'D'..='G' | 'J'..='Q' | 'S'..='Y' => {
char::from_u32(0x1D504 + (ch as u32 - 'A' as u32)).unwrap_or(ch)
}
'a'..='z' => char::from_u32(0x1D51E + (ch as u32 - 'a' as u32)).unwrap_or(ch),
_ => ch,
}
}
fn to_sans_serif(ch: char) -> char {
match ch {
'A'..='Z' => char::from_u32(0x1D5A0 + (ch as u32 - 'A' as u32)).unwrap_or(ch),
'a'..='z' => char::from_u32(0x1D5BA + (ch as u32 - 'a' as u32)).unwrap_or(ch),
'0'..='9' => char::from_u32(0x1D7E2 + (ch as u32 - '0' as u32)).unwrap_or(ch),
_ => ch,
}
}
fn to_monospace(ch: char) -> char {
match ch {
'A'..='Z' => char::from_u32(0x1D670 + (ch as u32 - 'A' as u32)).unwrap_or(ch),
'a'..='z' => char::from_u32(0x1D68A + (ch as u32 - 'a' as u32)).unwrap_or(ch),
'0'..='9' => char::from_u32(0x1D7F6 + (ch as u32 - '0' as u32)).unwrap_or(ch),
_ => ch,
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_bold() {
assert_eq!(to_bold('A'), '𝐀');
assert_eq!(to_bold('Z'), '𝐙');
assert_eq!(to_bold('a'), '𝐚');
assert_eq!(to_bold('z'), '𝐳');
assert_eq!(to_bold('0'), '𝟎');
}
#[test]
fn test_double_struck() {
assert_eq!(to_double_struck('R'), 'ℝ');
assert_eq!(to_double_struck('Z'), 'ℤ');
assert_eq!(to_double_struck('N'), 'ℕ');
assert_eq!(to_double_struck('C'), 'ℂ');
assert_eq!(to_double_struck('Q'), 'ℚ');
assert_eq!(to_double_struck('A'), '𝔸');
}
#[test]
fn test_script() {
assert_eq!(to_script('L'), 'ℒ');
assert_eq!(to_script('H'), 'ℋ');
assert_eq!(to_script('B'), 'ℬ');
assert_eq!(to_script('A'), '𝒜');
}
#[test]
fn test_fraktur() {
assert_eq!(to_fraktur('H'), 'ℌ');
assert_eq!(to_fraktur('R'), 'ℜ');
assert_eq!(to_fraktur('a'), '𝔞');
assert_eq!(to_fraktur('g'), '𝔤');
}
#[test]
fn test_sans_serif() {
assert_eq!(to_sans_serif('A'), '𝖠');
assert_eq!(to_sans_serif('a'), '𝖺');
}
#[test]
fn test_monospace() {
assert_eq!(to_monospace('A'), '𝙰');
assert_eq!(to_monospace('a'), '𝚊');
assert_eq!(to_monospace('0'), '𝟶');
}
#[test]
fn test_non_letter_passthrough() {
assert_eq!(map_char(&MathFontKind::Bold, '+'), '+');
assert_eq!(map_char(&MathFontKind::Blackboard, ' '), ' ');
}
}