unicode_font/
variant.rs

1//! Font variants.
2
3/// Font variants we deal with.
4///
5/// # Main feature
6///
7/// Implements `FromString` to parse a variant from the UNICODE name in English.
8#[derive(Debug, strum::Display, strum::EnumString, strum::EnumIter, Clone, Copy)]
9pub enum Variant {
10    #[strum(ascii_case_insensitive)]
11    ArabicMathematical,
12    #[strum(ascii_case_insensitive)]
13    ArabicMathematicalInitial,
14    #[strum(ascii_case_insensitive)]
15    Bold,
16    #[strum(ascii_case_insensitive)]
17    BoldItalic,
18    #[strum(ascii_case_insensitive)]
19    BoldFraktur,
20    #[strum(ascii_case_insensitive)]
21    BoldScript,
22    #[strum(ascii_case_insensitive)]
23    Circled,
24    #[strum(ascii_case_insensitive)]
25    Comma,
26    #[strum(ascii_case_insensitive)]
27    DoubleStruck,
28    #[strum(ascii_case_insensitive)]
29    DoubleStruckItalic,
30    #[strum(ascii_case_insensitive)]
31    Fraktur,
32    #[strum(ascii_case_insensitive)]
33    FullStop,
34    #[strum(ascii_case_insensitive)]
35    Fullwidth,
36    #[strum(ascii_case_insensitive)]
37    Italic,
38    #[strum(ascii_case_insensitive)]
39    Looped,
40    #[strum(ascii_case_insensitive)]
41    Monospace,
42    #[strum(ascii_case_insensitive)]
43    NegativeCircled,
44    #[strum(ascii_case_insensitive)]
45    NegativeSquared,
46    #[strum(ascii_case_insensitive)]
47    Regional,
48    #[strum(ascii_case_insensitive)]
49    Segmented,
50    /// Few other font variants.
51    /// 
52    /// These are special cases that are marked as font variant in the Unicode standard,
53    /// but there was no clear named variant to map it to.
54    #[strum(ascii_case_insensitive)]
55    Other,
56    #[strum(ascii_case_insensitive)]
57    Parenthesized,
58    #[strum(ascii_case_insensitive)]
59    Plain,
60    #[strum(ascii_case_insensitive)]
61    SansSerifBoldItalic,
62    #[strum(ascii_case_insensitive)]
63    SansSerifBold,
64    #[strum(ascii_case_insensitive)]
65    SansSerifItalic,
66    #[strum(ascii_case_insensitive)]
67    SansSerif,
68    #[strum(ascii_case_insensitive)]
69    MathematicalScript,
70    #[strum(ascii_case_insensitive)]
71    Script,
72    #[strum(ascii_case_insensitive)]
73    SmallCapital,
74    #[strum(ascii_case_insensitive)]
75    Superscript,
76    #[strum(ascii_case_insensitive)]
77    Subscript,
78    #[strum(ascii_case_insensitive)]
79    Stretched,
80    #[strum(ascii_case_insensitive)]
81    Squared,
82    #[strum(ascii_case_insensitive)]
83    Tailed,
84    #[strum(ascii_case_insensitive)]
85    Wide,
86}
87
88#[derive(Debug)]
89pub struct ParseError {
90    pub name: String,
91}
92
93impl core::fmt::Display for ParseError {
94    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
95        write!(f, "unclassified name:\n{}", self.name)
96    }
97}
98impl std::error::Error for ParseError {}
99
100impl Variant {
101    /// Converts a Unicode name into a Variant.
102    ///
103    /// # Errors
104    ///
105    /// If the name is unclassified.
106    pub fn parse_standard_name(s: &str) -> Result<Self, ParseError> {
107        let s = s.to_lowercase();
108        if s.contains("superscript") | s.contains("modifier") {
109            return Ok(Variant::Superscript);
110        }
111        if s.contains("subscript") {
112            return Ok(Variant::Subscript);
113        }
114        if s.contains("fullwidth") {
115            return Ok(Variant::Fullwidth);
116        }
117        if s.contains("small capital") {
118            return Ok(Variant::SmallCapital);
119        }
120        if s.contains("sans-serif bold italic") {
121            return Ok(Variant::SansSerifBoldItalic);
122        }
123        if s.contains("sans-serif bold") {
124            return Ok(Variant::SansSerifBold);
125        }
126        if s.contains("sans-serif italic") {
127            return Ok(Variant::SansSerifItalic);
128        }
129        if s.contains("sans-serif") {
130            return Ok(Variant::SansSerif);
131        }
132        if s.contains("bold italic") {
133            return Ok(Variant::BoldItalic);
134        }
135        if s.contains("bold script") {
136            return Ok(Variant::BoldScript);
137        }
138        if s.contains("mathematical script") {
139            return Ok(Variant::MathematicalScript);
140        }
141        if ["planck constant", "planck constant over two pi"].contains(&s.as_str()) | s.contains("script") {
142            return Ok(Variant::Script);
143        }
144        if s.contains("bold fraktur") {
145            return Ok(Variant::BoldFraktur);
146        }
147        if s.contains("fraktur") | s.contains("black-letter") {
148            return Ok(Variant::Fraktur);
149        }
150        if s.contains("double-struck italic") | s.contains("doublestruck italic") {
151            return Ok(Variant::DoubleStruckItalic);
152        }
153        if s.contains("double-struck") | s.contains("doublestruck") {
154            return Ok(Variant::DoubleStruck);
155        }
156        if s.contains("bold") | s.contains("heavy") {
157            return Ok(Variant::Bold);
158        }
159        if s.contains("italic") {
160            return Ok(Variant::Italic);
161        }
162        if s.contains("monospace") {
163            return Ok(Variant::Monospace);
164        }
165        if s.contains("comma") {
166            return Ok(Variant::Comma);
167        }
168        if s.contains("full stop") {
169            return Ok(Variant::FullStop);
170        }
171        if s.contains("negative circled") {
172            return Ok(Variant::NegativeCircled);
173        }
174        if s.contains("negative squared") {
175            return Ok(Variant::NegativeSquared);
176        }
177        if s.contains("circled") {
178            return Ok(Variant::Circled);
179        }
180        if s.contains("squared") {
181            return Ok(Variant::Squared);
182        }
183        if s.contains("regional") {
184            return Ok(Variant::Regional);
185        }
186        if ["information source", "hebrew letter alternative ayin", "hebrew letter alternative plus sign"].contains(&s.as_str())  {
187            return Ok(Variant::Other);
188        }
189        if s.contains("wide") {
190            return Ok(Variant::Wide);
191        }
192        if s.contains("looped") {
193            return Ok(Variant::Looped);
194        }
195        if s.contains("stretched") {
196            return Ok(Variant::Stretched);
197        }
198        if s.contains("tailed") {
199            return Ok(Variant::Tailed);
200        }
201        if s.contains("arabic mathematical initial") {
202            return Ok(Variant::ArabicMathematicalInitial);
203        }
204        if s.contains("arabic mathematical") {
205            return Ok(Variant::ArabicMathematical);
206        }
207        if s.contains("segmented") {
208            return Ok(Variant::Segmented);
209        }
210        if s.contains("parenthesized") {
211            return Ok(Variant::Parenthesized);
212        }
213        if s.contains("small") {
214            return Ok(Variant::SmallCapital);
215        }
216
217        Err(ParseError {
218            name: s.to_string(),
219        })
220    }
221}