fea_rs/common/
glyph_map.rs1use write_fonts::tables::post::Post;
2
3use super::{GlyphId16, GlyphIdent};
4use fontdrasil::types::GlyphName;
5use std::{
6 borrow::Cow,
7 collections::{BTreeMap, HashMap},
8 convert::TryInto,
9 iter::FromIterator,
10};
11
12#[derive(Clone, Debug, Default, PartialEq)]
20#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
21pub struct GlyphMap {
22 names: HashMap<GlyphName, GlyphId16>,
23 cids: HashMap<u16, GlyphId16>,
24}
25
26impl GlyphMap {
27 pub fn len(&self) -> usize {
29 self.names.len() + self.cids.len()
30 }
31
32 pub fn is_empty(&self) -> bool {
34 self.names.is_empty() && self.cids.is_empty()
35 }
36
37 pub fn reverse_map(&self) -> BTreeMap<GlyphId16, GlyphIdent> {
40 self.names
41 .iter()
42 .map(|(name, id)| (*id, GlyphIdent::Name(name.clone())))
43 .chain(
44 self.cids
45 .iter()
46 .map(|(cid, id)| (*id, GlyphIdent::Cid(*cid))),
47 )
48 .collect()
49 }
50
51 pub fn iter(&self) -> impl Iterator<Item = GlyphIdent> + '_ {
55 self.reverse_map().into_values()
56 }
57
58 pub fn contains<Q: ?Sized + sealed::AsGlyphIdent>(&self, key: &Q) -> bool {
60 if let Some(name) = key.named() {
61 self.names.contains_key(name)
62 } else if let Some(cid) = key.cid() {
63 self.cids.contains_key(cid)
64 } else {
65 unreachable!()
66 }
67 }
68
69 pub fn get<Q: ?Sized + sealed::AsGlyphIdent>(&self, key: &Q) -> Option<GlyphId16> {
71 if let Some(name) = key.named() {
72 self.names.get(name).copied()
73 } else if let Some(cid) = key.cid() {
74 self.cids.get(cid).copied()
75 } else {
76 unreachable!()
77 }
78 }
79
80 pub fn make_post_table(&self) -> Post {
82 let reverse = self.reverse_map();
83 let rev_vec = reverse
84 .values()
85 .map(|val| match val {
86 GlyphIdent::Name(s) => Cow::Borrowed(s.as_str()),
87 GlyphIdent::Cid(cid) => Cow::Owned(format!("cid{:05}", *cid)),
88 })
89 .collect::<Vec<_>>();
90
91 Post::new_v2(rev_vec.iter().map(Cow::as_ref))
92 }
93}
94
95impl FromIterator<u16> for GlyphMap {
96 fn from_iter<T: IntoIterator<Item = u16>>(iter: T) -> Self {
97 GlyphMap {
98 names: HashMap::new(),
99 cids: iter
100 .into_iter()
101 .enumerate()
102 .map(|(i, cid)| (cid, GlyphId16::new(i.try_into().unwrap())))
103 .collect(),
104 }
105 }
106}
107
108impl FromIterator<GlyphName> for GlyphMap {
109 fn from_iter<T: IntoIterator<Item = GlyphName>>(iter: T) -> Self {
110 GlyphMap {
111 names: iter
112 .into_iter()
113 .enumerate()
114 .map(|(i, cid)| (cid, GlyphId16::new(i.try_into().unwrap())))
115 .collect(),
116 cids: HashMap::new(),
117 }
118 }
119}
120
121impl FromIterator<GlyphIdent> for GlyphMap {
123 fn from_iter<T: IntoIterator<Item = GlyphIdent>>(iter: T) -> Self {
124 let mut names = HashMap::new();
125 let mut cids = HashMap::new();
126 for (idx, item) in iter.into_iter().enumerate() {
127 let idx = GlyphId16::new(idx.try_into().unwrap());
128 match item {
129 GlyphIdent::Cid(cid) => cids.insert(cid, idx),
130 GlyphIdent::Name(name) => names.insert(name, idx),
131 };
132 }
133 GlyphMap { names, cids }
134 }
135}
136
137mod sealed {
138 use super::super::GlyphIdent;
139 use fontdrasil::types::GlyphName;
140 use smol_str::SmolStr;
141
142 pub trait AsGlyphIdent {
149 fn named(&self) -> Option<&str> {
150 None
151 }
152
153 fn cid(&self) -> Option<&u16> {
154 None
155 }
156 }
157
158 impl AsGlyphIdent for str {
159 fn named(&self) -> Option<&str> {
160 Some(self)
161 }
162 }
163
164 impl AsGlyphIdent for SmolStr {
165 fn named(&self) -> Option<&str> {
166 Some(self.as_str())
167 }
168 }
169
170 impl AsGlyphIdent for GlyphName {
171 fn named(&self) -> Option<&str> {
172 Some(self.as_str())
173 }
174 }
175
176 impl AsGlyphIdent for u16 {
177 fn cid(&self) -> Option<&u16> {
178 Some(self)
179 }
180 }
181
182 impl AsGlyphIdent for GlyphIdent {
183 fn named(&self) -> Option<&str> {
184 if let GlyphIdent::Name(name) = self {
185 Some(name.as_str())
186 } else {
187 None
188 }
189 }
190
191 fn cid(&self) -> Option<&u16> {
192 if let GlyphIdent::Cid(cid) = self {
193 Some(cid)
194 } else {
195 None
196 }
197 }
198 }
199}