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)]
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 len(&self) -> usize {
35 self.names.len() + self.cids.len()
36 }
37
38 pub fn is_empty(&self) -> bool {
40 self.names.is_empty() && self.cids.is_empty()
41 }
42
43 pub fn reverse_map(&self) -> BTreeMap<GlyphId16, GlyphIdent> {
46 self.names
47 .iter()
48 .map(|(name, id)| (*id, GlyphIdent::Name(name.clone())))
49 .chain(
50 self.cids
51 .iter()
52 .map(|(cid, id)| (*id, GlyphIdent::Cid(*cid))),
53 )
54 .collect()
55 }
56
57 pub fn iter(&self) -> impl Iterator<Item = GlyphIdent> + '_ {
61 self.reverse_map().into_values()
62 }
63
64 pub fn contains<Q: ?Sized + sealed::AsGlyphIdent>(&self, key: &Q) -> bool {
66 if let Some(name) = key.named() {
67 self.names.contains_key(name)
68 } else if let Some(cid) = key.cid() {
69 self.cids.contains_key(cid)
70 } else {
71 unreachable!()
72 }
73 }
74
75 pub fn get<Q: ?Sized + sealed::AsGlyphIdent>(&self, key: &Q) -> Option<GlyphId16> {
77 if let Some(name) = key.named() {
78 self.names.get(name).copied()
79 } else if let Some(cid) = key.cid() {
80 self.cids.get(cid).copied()
81 } else {
82 unreachable!()
83 }
84 }
85
86 pub fn make_post_table(&self) -> Post {
88 let reverse = self.reverse_map();
89 let rev_vec = reverse
90 .values()
91 .map(|val| match val {
92 GlyphIdent::Name(s) => Cow::Borrowed(s.as_str()),
93 GlyphIdent::Cid(cid) => Cow::Owned(format!("cid{:05}", *cid)),
94 })
95 .collect::<Vec<_>>();
96
97 Post::new_v2(rev_vec.iter().map(Cow::as_ref))
98 }
99}
100
101impl<T: Into<GlyphIdent>> FromIterator<T> for GlyphMap {
102 fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
103 let mut names = HashMap::new();
104 let mut cids = HashMap::new();
105 for (idx, item) in iter.into_iter().enumerate() {
106 let idx = GlyphId16::new(idx.try_into().unwrap());
107 match item.into() {
108 GlyphIdent::Cid(cid) => cids.insert(cid, idx),
109 GlyphIdent::Name(name) => names.insert(name, idx),
110 };
111 }
112 GlyphMap { names, cids }
113 }
114}
115
116mod sealed {
117 use super::super::GlyphIdent;
118 use fontdrasil::types::GlyphName;
119 use smol_str::SmolStr;
120
121 pub trait AsGlyphIdent {
128 fn named(&self) -> Option<&str> {
129 None
130 }
131
132 fn cid(&self) -> Option<&u16> {
133 None
134 }
135 }
136
137 impl AsGlyphIdent for str {
138 fn named(&self) -> Option<&str> {
139 Some(self)
140 }
141 }
142
143 impl AsGlyphIdent for SmolStr {
144 fn named(&self) -> Option<&str> {
145 Some(self.as_str())
146 }
147 }
148
149 impl AsGlyphIdent for GlyphName {
150 fn named(&self) -> Option<&str> {
151 Some(self.as_str())
152 }
153 }
154
155 impl AsGlyphIdent for u16 {
156 fn cid(&self) -> Option<&u16> {
157 Some(self)
158 }
159 }
160
161 impl AsGlyphIdent for GlyphIdent {
162 fn named(&self) -> Option<&str> {
163 if let GlyphIdent::Name(name) = self {
164 Some(name.as_str())
165 } else {
166 None
167 }
168 }
169
170 fn cid(&self) -> Option<&u16> {
171 if let GlyphIdent::Cid(cid) = self {
172 Some(cid)
173 } else {
174 None
175 }
176 }
177 }
178}