1use std::ops::Range;
2
3use fea_rs::typed::{AstNode as _, GlyphOrClass};
4use smol_str::SmolStr;
5
6use crate::AsFea;
7
8const FEA_KEYWORDS: [&str; 52] = [
9 "anchor",
10 "anchordef",
11 "anon",
12 "anonymous",
13 "by",
14 "contour",
15 "cursive",
16 "device",
17 "enum",
18 "enumerate",
19 "excludedflt",
20 "exclude_dflt",
21 "feature",
22 "from",
23 "ignore",
24 "ignorebaseglyphs",
25 "ignoreligatures",
26 "ignoremarks",
27 "include",
28 "includedflt",
29 "include_dflt",
30 "language",
31 "languagesystem",
32 "lookup",
33 "lookupflag",
34 "mark",
35 "markattachmenttype",
36 "markclass",
37 "nameid",
38 "null",
39 "parameters",
40 "pos",
41 "position",
42 "required",
43 "righttoleft",
44 "reversesub",
45 "rsub",
46 "script",
47 "sub",
48 "substitute",
49 "subtable",
50 "table",
51 "usemarkfilteringset",
52 "useextension",
53 "valuerecorddef",
54 "base",
55 "gdef",
56 "head",
57 "hhea",
58 "name",
59 "vhea",
60 "vmtx",
61];
62
63#[derive(Clone, PartialEq, Eq, Hash)]
65pub struct GlyphName {
66 pub name: SmolStr,
68}
69
70impl std::fmt::Debug for GlyphName {
71 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
72 write!(f, "GlyphName({})", self.name)
73 }
74}
75
76impl GlyphName {
77 pub fn new(name: &str) -> Self {
79 Self {
80 name: SmolStr::new(name),
81 }
82 }
83
84 pub fn glyphset(&self) -> impl Iterator<Item = &SmolStr> {
86 std::iter::once(&self.name)
87 }
88}
89impl AsFea for GlyphName {
90 fn as_fea(&self, _indent: &str) -> String {
91 if FEA_KEYWORDS.contains(&self.name.as_str()) {
92 format!("\\{}", self.name)
93 } else {
94 self.name.to_string()
95 }
96 }
97}
98
99#[derive(Debug, Clone, PartialEq, Eq, Hash)]
101pub struct GlyphClass {
102 pub glyphs: Vec<GlyphContainer>,
104 pub location: Range<usize>,
106}
107impl GlyphClass {
108 pub fn new(glyphs: Vec<GlyphContainer>, location: Range<usize>) -> Self {
110 Self { glyphs, location }
111 }
112}
113impl AsFea for GlyphClass {
114 fn as_fea(&self, _indent: &str) -> String {
115 let inner: Vec<String> = self.glyphs.iter().map(|g| g.as_fea("")).collect();
116 format!("[{}]", inner.join(" "))
117 }
118}
119impl From<fea_rs::typed::GlyphClass> for GlyphClass {
120 fn from(val: fea_rs::typed::GlyphClass) -> Self {
121 match val {
122 fea_rs::typed::GlyphClass::Named(glyph_class_name) => {
123 let members = vec![GlyphContainer::GlyphClassName(SmolStr::new(
124 glyph_class_name.text(),
125 ))];
126 GlyphClass::new(members, glyph_class_name.range())
127 }
128 fea_rs::typed::GlyphClass::Literal(glyph_class_literal) => glyph_class_literal.into(),
129 }
130 }
131}
132impl From<fea_rs::typed::GlyphClassLiteral> for GlyphClass {
133 fn from(val: fea_rs::typed::GlyphClassLiteral) -> Self {
134 let members: Vec<GlyphContainer> = val
135 .node()
136 .iter_children()
137 .flat_map(|child| {
138 if let Some(gc) = fea_rs::typed::GlyphOrClass::cast(child) {
139 Some(gc.into())
140 } else if let Some(gr) = fea_rs::typed::GlyphRange::cast(child) {
141 let start = gr
142 .iter()
143 .find_map(fea_rs::typed::GlyphName::cast)
144 .map(|gn| SmolStr::new(gn.text()))
145 .unwrap();
146 let end = gr
147 .iter()
148 .skip_while(|t| t.kind() != fea_rs::Kind::Hyphen)
149 .find_map(fea_rs::typed::GlyphName::cast)
150 .map(|gn| SmolStr::new(gn.text()))
151 .unwrap();
152
153 Some(GlyphContainer::GlyphRange(GlyphRange::new(start, end)))
154 } else if child.kind() == fea_rs::Kind::GlyphNameOrRange {
156 Some(GlyphContainer::GlyphNameOrRange(
157 child.token_text().unwrap().into(),
158 ))
159 } else {
160 None
161 }
162 })
163 .collect();
164 GlyphClass::new(members, val.node().range())
165 }
166}
167
168#[derive(Debug, Clone, PartialEq, Eq, Hash)]
170pub struct GlyphRange {
171 pub start: SmolStr,
173 pub end: SmolStr,
175}
176impl GlyphRange {
177 pub fn new(start: SmolStr, end: SmolStr) -> Self {
179 Self { start, end }
180 }
181
182 pub fn glyphset(&self) -> impl Iterator<Item = SmolStr> {
184 let start_bytes = self.start.as_bytes();
190 let end_bytes = self.end.as_bytes();
191 let mut prefix_len = 0;
192 let mut suffix_len = 0;
193 for (start_byte, end_byte) in start_bytes.iter().zip(end_bytes.iter()) {
194 if start_byte == end_byte {
195 prefix_len += 1;
196 } else {
197 break;
198 }
199 }
200 for (start_byte, end_byte) in start_bytes.iter().rev().zip(end_bytes.iter().rev()) {
201 if start_byte == end_byte {
202 suffix_len += 1;
203 } else {
204 break;
205 }
206 }
207 let start_core = &self.start[prefix_len..self.start.len() - suffix_len];
208 let end_core = &self.end[prefix_len..self.end.len() - suffix_len];
209 if start_core.len() != end_core.len() {
210 return vec![self.start.clone(), self.end.clone()].into_iter();
212 }
213 if start_core.len() == 1 {
214 let start_char = start_core.chars().next().unwrap();
216 let end_char = end_core.chars().next().unwrap();
217 if (start_char.is_ascii_lowercase() && end_char.is_ascii_lowercase())
218 || (start_char.is_ascii_uppercase() && end_char.is_ascii_uppercase())
219 {
220 let range = start_char as u8..=end_char as u8;
221 let glyphs: Vec<SmolStr> = range
222 .map(|b| {
223 SmolStr::new(format!(
224 "{}{}{}",
225 &self.start[..prefix_len],
226 b as char,
227 &self.start[self.start.len() - suffix_len..]
228 ))
229 })
230 .collect();
231 return glyphs.into_iter();
232 } else {
233 return vec![self.start.clone(), self.end.clone()].into_iter();
235 }
236 }
237 let start_num: Option<usize> = start_core.parse().ok();
239 let end_num: Option<usize> = end_core.parse().ok();
240 if let (Some(start_num), Some(end_num)) = (start_num, end_num) {
241 let range = start_num..=end_num;
242 let glyphs: Vec<SmolStr> = range
244 .map(|n| {
245 let mut s = String::new();
246 s.push_str(&self.start[..prefix_len]);
247 s.push_str(&format!("{:0width$}", n, width = start_core.len()));
248 s.push_str(&self.start[self.start.len() - suffix_len..]);
249 SmolStr::new(s)
250 })
251 .collect();
252 return glyphs.into_iter();
253 }
254 vec![self.start.clone(), self.end.clone()].into_iter()
256 }
257}
258impl From<Range<SmolStr>> for GlyphRange {
259 fn from(val: Range<SmolStr>) -> Self {
260 GlyphRange::new(val.start, val.end)
261 }
262}
263impl AsFea for GlyphRange {
264 fn as_fea(&self, _indent: &str) -> String {
265 format!("{} - {}", self.start.as_str(), self.end.as_str())
266 }
267}
268
269#[derive(Debug, Clone, PartialEq, Eq, Hash)]
272pub enum GlyphContainer {
273 GlyphName(GlyphName),
275 GlyphClass(GlyphClass),
277 GlyphClassName(SmolStr),
279 GlyphRange(GlyphRange),
281 GlyphNameOrRange(SmolStr),
283}
284
285impl AsFea for GlyphContainer {
286 fn as_fea(&self, _indent: &str) -> String {
287 match self {
288 GlyphContainer::GlyphName(gn) => gn.as_fea(_indent),
289 GlyphContainer::GlyphClass(gcs) => {
290 let inner: Vec<String> = gcs.glyphs.iter().map(|g| g.as_fea("")).collect();
291 format!("[{}]", inner.join(" "))
292 }
293 GlyphContainer::GlyphClassName(name) => {
294 if FEA_KEYWORDS.contains(&name.as_str()) {
295 format!("\\{}", name)
296 } else {
297 name.to_string()
298 }
299 }
300 GlyphContainer::GlyphRange(range) => range.as_fea(""),
301 GlyphContainer::GlyphNameOrRange(name_or_range) => name_or_range.to_string(),
302 }
303 }
304}
305
306impl From<fea_rs::typed::GlyphOrClass> for GlyphContainer {
307 fn from(val: fea_rs::typed::GlyphOrClass) -> Self {
308 match val {
309 GlyphOrClass::Glyph(glyph) => GlyphContainer::GlyphName(GlyphName::new(glyph.text())),
310 GlyphOrClass::Class(glyph_class_literal) => {
311 GlyphContainer::GlyphClass(glyph_class_literal.into())
312 }
313 GlyphOrClass::Cid(_cid) => todo!(),
314 GlyphOrClass::NamedClass(glyph_class_name) => {
315 GlyphContainer::GlyphClassName(SmolStr::new(glyph_class_name.text()))
316 }
317 GlyphOrClass::Null(_) => GlyphContainer::GlyphName(GlyphName::new("NULL")),
318 }
319 }
320}
321
322impl From<fea_rs::typed::GlyphClass> for GlyphContainer {
323 fn from(val: fea_rs::typed::GlyphClass) -> Self {
324 match val {
325 fea_rs::typed::GlyphClass::Named(glyph_class_name) => {
326 GlyphContainer::GlyphClassName(SmolStr::new(glyph_class_name.text()))
327 }
328 fea_rs::typed::GlyphClass::Literal(glyph_class_literal) => {
329 GlyphContainer::GlyphClass(glyph_class_literal.into())
330 }
331 }
332 }
333}
334
335impl GlyphContainer {
336 pub fn new_class(glyph_names: &[&str]) -> Self {
339 let members: Vec<GlyphContainer> = glyph_names
340 .iter()
341 .map(|name| GlyphContainer::GlyphName(GlyphName::new(name)))
342 .collect();
343 GlyphContainer::GlyphClass(GlyphClass::new(
344 members,
345 0..0, ))
347 }
348
349 pub fn is_empty(&self) -> bool {
351 match self {
352 GlyphContainer::GlyphClass(gcs) => gcs.glyphs.is_empty(),
353 _ => false,
354 }
355 }
356}
357
358#[derive(Debug, Clone, PartialEq, Eq, Hash)]
369pub struct MarkClass {
370 pub name: SmolStr,
372}
373impl MarkClass {
374 pub fn new(name: &str) -> Self {
376 Self {
377 name: SmolStr::new(name),
378 }
379 }
380}
381impl From<fea_rs::typed::GlyphClassDef> for MarkClass {
382 fn from(val: fea_rs::typed::GlyphClassDef) -> Self {
383 let label = val
384 .iter()
385 .find_map(fea_rs::typed::GlyphClassName::cast)
386 .unwrap();
387 MarkClass::new(label.text().trim_start_matches('@'))
388 }
389}
390
391#[cfg(test)]
392mod tests {
393 use super::*;
394
395 #[test]
396 fn test_glyphclass() {
397 const FEA: &str = "@foo = [a-b c d-e @bar];";
398 let (parsed, _) = fea_rs::parse::parse_string(FEA);
399 let definition = parsed
400 .root()
401 .iter_children()
402 .find_map(fea_rs::typed::GlyphClassDef::cast)
403 .unwrap();
404 let literal = definition
405 .node()
406 .iter_children()
407 .find_map(fea_rs::typed::GlyphClassLiteral::cast)
408 .unwrap();
409 println!("{:#?}", literal);
410 let glyphs: GlyphClass = literal.into();
411 assert_eq!(glyphs.glyphs.len(), 4);
412 }
413
414 #[test]
415 fn test_glyphrange() {
416 let range = GlyphRange::new(SmolStr::new("a01"), SmolStr::new("a05"));
417 let glyphs: Vec<SmolStr> = range.glyphset().collect();
418 assert_eq!(
419 glyphs,
420 vec![
421 SmolStr::new("a01"),
422 SmolStr::new("a02"),
423 SmolStr::new("a03"),
424 SmolStr::new("a04"),
425 SmolStr::new("a05"),
426 ]
427 );
428
429 let range = GlyphRange::new(SmolStr::new("B"), SmolStr::new("F"));
430 let glyphs: Vec<SmolStr> = range.glyphset().collect();
431 assert_eq!(
432 glyphs,
433 vec![
434 SmolStr::new("B"),
435 SmolStr::new("C"),
436 SmolStr::new("D"),
437 SmolStr::new("E"),
438 SmolStr::new("F"),
439 ]
440 );
441
442 let range = GlyphRange::new(SmolStr::new("cat"), SmolStr::new("cet"));
443 let glyphs: Vec<SmolStr> = range.glyphset().collect();
444 assert_eq!(
445 glyphs,
446 vec![
447 SmolStr::new("cat"),
448 SmolStr::new("cbt"),
449 SmolStr::new("cct"),
450 SmolStr::new("cdt"),
451 SmolStr::new("cet"),
452 ]
453 );
454 }
455}