fea_rs/common/
glyph_map.rs1use write_fonts::tables::post::Post;
2
3use crate::compile::error::GlyphOrderError;
4
5use super::{GlyphId16, GlyphIdent};
6use fontdrasil::types::GlyphName;
7use std::{
8 borrow::Cow,
9 collections::{BTreeMap, HashMap},
10};
11
12#[derive(Clone, Debug, Default, PartialEq)]
26#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
27pub struct GlyphMap {
28 names: HashMap<GlyphName, GlyphId16>,
29 cids: HashMap<u16, GlyphId16>,
30}
31
32impl GlyphMap {
33 pub fn new<T, I>(iter: I) -> Result<Self, GlyphOrderError>
37 where
38 T: Into<GlyphIdent>,
39 I: IntoIterator<Item = T>,
40 {
41 let mut names = HashMap::new();
42 let mut cids = HashMap::new();
43 for (idx, item) in iter.into_iter().enumerate() {
44 let idx = u16::try_from(idx)
45 .map(GlyphId16::new)
46 .map_err(|_| GlyphOrderError::TooManyGlyphs { found: idx as _ })?;
47 match item.into() {
48 GlyphIdent::Cid(cid) => cids.insert(cid, idx),
49 GlyphIdent::Name(name) => names.insert(name, idx),
50 };
51 }
52 Ok(GlyphMap { names, cids })
53 }
54 pub fn len(&self) -> usize {
56 self.names.len() + self.cids.len()
57 }
58
59 pub fn is_empty(&self) -> bool {
61 self.names.is_empty() && self.cids.is_empty()
62 }
63
64 pub fn reverse_map(&self) -> BTreeMap<GlyphId16, GlyphIdent> {
67 self.names
68 .iter()
69 .map(|(name, id)| (*id, GlyphIdent::Name(name.clone())))
70 .chain(
71 self.cids
72 .iter()
73 .map(|(cid, id)| (*id, GlyphIdent::Cid(*cid))),
74 )
75 .collect()
76 }
77
78 pub fn iter(&self) -> impl Iterator<Item = GlyphIdent> + '_ {
82 self.reverse_map().into_values()
83 }
84
85 pub fn contains<Q: ?Sized + sealed::AsGlyphIdent>(&self, key: &Q) -> bool {
87 if let Some(name) = key.named() {
88 self.names.contains_key(name)
89 } else if let Some(cid) = key.cid() {
90 self.cids.contains_key(cid)
91 } else {
92 unreachable!()
93 }
94 }
95
96 pub fn get<Q: ?Sized + sealed::AsGlyphIdent>(&self, key: &Q) -> Option<GlyphId16> {
98 if let Some(name) = key.named() {
99 self.names.get(name).copied()
100 } else if let Some(cid) = key.cid() {
101 self.cids.get(cid).copied()
102 } else {
103 unreachable!()
104 }
105 }
106
107 pub fn make_post_table(&self) -> Post {
109 let reverse = self.reverse_map();
110 let rev_vec = reverse
111 .values()
112 .map(|val| match val {
113 GlyphIdent::Name(s) => Cow::Borrowed(s.as_str()),
114 GlyphIdent::Cid(cid) => Cow::Owned(format!("cid{:05}", *cid)),
115 })
116 .collect::<Vec<_>>();
117
118 Post::new_v2(rev_vec.iter().map(Cow::as_ref))
119 }
120}
121
122mod sealed {
123 use super::super::GlyphIdent;
124 use fontdrasil::types::GlyphName;
125 use smol_str::SmolStr;
126
127 pub trait AsGlyphIdent {
134 fn named(&self) -> Option<&str> {
135 None
136 }
137
138 fn cid(&self) -> Option<&u16> {
139 None
140 }
141 }
142
143 impl AsGlyphIdent for str {
144 fn named(&self) -> Option<&str> {
145 Some(self)
146 }
147 }
148
149 impl AsGlyphIdent for SmolStr {
150 fn named(&self) -> Option<&str> {
151 Some(self.as_str())
152 }
153 }
154
155 impl AsGlyphIdent for GlyphName {
156 fn named(&self) -> Option<&str> {
157 Some(self.as_str())
158 }
159 }
160
161 impl AsGlyphIdent for u16 {
162 fn cid(&self) -> Option<&u16> {
163 Some(self)
164 }
165 }
166
167 impl AsGlyphIdent for GlyphIdent {
168 fn named(&self) -> Option<&str> {
169 if let GlyphIdent::Name(name) = self {
170 Some(name.as_str())
171 } else {
172 None
173 }
174 }
175
176 fn cid(&self) -> Option<&u16> {
177 if let GlyphIdent::Cid(cid) = self {
178 Some(cid)
179 } else {
180 None
181 }
182 }
183 }
184}