1use core::ops::Range;
4use raw::{
5 tables::{
6 cff::Cff,
7 post::Post,
8 postscript::{Charset, CharsetIter, StringId as Sid},
9 },
10 types::GlyphId,
11 FontRef, TableProvider,
12};
13
14const MAX_GLYPH_NAME_LEN: usize = 63;
18
19#[derive(Clone)]
26pub struct GlyphNames<'a> {
27 inner: Inner<'a>,
28}
29
30#[derive(Clone)]
31enum Inner<'a> {
32 Post(Post<'a>, u32),
34 Cff(Cff<'a>, Charset<'a>),
35 Synthesized(u32),
36}
37
38impl<'a> GlyphNames<'a> {
39 pub fn new(font: &FontRef<'a>) -> Self {
41 let num_glyphs = font
42 .maxp()
43 .map(|maxp| maxp.num_glyphs() as u32)
44 .unwrap_or_default();
45 if let Ok(post) = font.post() {
46 if post.num_names() != 0 {
47 return Self {
48 inner: Inner::Post(post, num_glyphs),
49 };
50 }
51 }
52 if let Some((cff, charset)) = font
53 .cff()
54 .ok()
55 .and_then(|cff| Some((cff.clone(), cff.charset(0).ok()??)))
56 {
57 return Self {
58 inner: Inner::Cff(cff, charset),
59 };
60 }
61 Self {
62 inner: Inner::Synthesized(num_glyphs),
63 }
64 }
65
66 pub fn source(&self) -> GlyphNameSource {
68 match &self.inner {
69 Inner::Post(..) => GlyphNameSource::Post,
70 Inner::Cff(..) => GlyphNameSource::Cff,
71 Inner::Synthesized(..) => GlyphNameSource::Synthesized,
72 }
73 }
74
75 pub fn num_glyphs(&self) -> u32 {
77 match &self.inner {
78 Inner::Post(_, n) | Inner::Synthesized(n) => *n,
79 Inner::Cff(_, charset) => charset.num_glyphs(),
80 }
81 }
82
83 pub fn get(&self, glyph_id: GlyphId) -> Option<GlyphName> {
85 if glyph_id.to_u32() >= self.num_glyphs() {
86 return None;
87 }
88 let name = match &self.inner {
89 Inner::Post(post, _) => GlyphName::from_post(post, glyph_id),
90 Inner::Cff(cff, charset) => charset
91 .string_id(glyph_id)
92 .ok()
93 .and_then(|sid| GlyphName::from_cff_sid(cff, sid)),
94 _ => None,
95 };
96 Some(name.unwrap_or_else(|| GlyphName::synthesize(glyph_id)))
97 }
98
99 pub fn iter(&self) -> impl Iterator<Item = (GlyphId, GlyphName)> + 'a + Clone {
102 match &self.inner {
103 Inner::Post(post, n) => Iter::Post(0..*n, post.clone()),
104 Inner::Cff(cff, charset) => Iter::Cff(cff.clone(), charset.iter()),
105 Inner::Synthesized(n) => Iter::Synthesized(0..*n),
106 }
107 }
108}
109
110#[derive(Copy, Clone, PartialEq, Eq, Debug)]
112pub enum GlyphNameSource {
113 Post,
115 Cff,
117 Synthesized,
120}
121
122#[derive(Clone)]
124pub struct GlyphName {
125 name: [u8; MAX_GLYPH_NAME_LEN],
126 len: u8,
127 is_synthesized: bool,
128}
129
130impl GlyphName {
131 pub fn as_str(&self) -> &str {
133 let bytes = &self.name[..self.len as usize];
134 core::str::from_utf8(bytes).unwrap_or_default()
135 }
136
137 pub fn is_synthesized(&self) -> bool {
140 self.is_synthesized
141 }
142
143 fn from_bytes(bytes: &[u8]) -> Self {
144 let mut name = Self::default();
145 name.append(bytes);
146 name
147 }
148
149 fn from_post(post: &Post, glyph_id: GlyphId) -> Option<Self> {
150 glyph_id
151 .try_into()
152 .ok()
153 .and_then(|id| post.glyph_name(id))
154 .map(|s| s.as_bytes())
155 .map(Self::from_bytes)
156 }
157
158 fn from_cff_sid(cff: &Cff, sid: Sid) -> Option<Self> {
159 cff.string(sid)
160 .and_then(|s| core::str::from_utf8(s.bytes()).ok())
161 .map(|s| s.as_bytes())
162 .map(Self::from_bytes)
163 }
164
165 fn synthesize(glyph_id: GlyphId) -> Self {
166 use core::fmt::Write;
167 let mut name = Self {
168 is_synthesized: true,
169 ..Self::default()
170 };
171 let _ = write!(GlyphNameWrite(&mut name), "gid{}", glyph_id.to_u32());
172 name
173 }
174
175 fn append(&mut self, bytes: &[u8]) {
183 let start = self.len as usize;
186 let available = MAX_GLYPH_NAME_LEN - start;
187 let copy_len = available.min(bytes.len());
188 self.name[start..start + copy_len].copy_from_slice(&bytes[..copy_len]);
189 self.len = (start + copy_len) as u8;
190 }
191}
192
193impl Default for GlyphName {
194 fn default() -> Self {
195 Self {
196 name: [0; MAX_GLYPH_NAME_LEN],
197 len: 0,
198 is_synthesized: false,
199 }
200 }
201}
202
203impl core::fmt::Debug for GlyphName {
204 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
205 f.debug_struct("GlyphName")
206 .field("name", &self.as_str())
207 .field("is_synthesized", &self.is_synthesized)
208 .finish()
209 }
210}
211
212impl core::fmt::Display for GlyphName {
213 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
214 write!(f, "{}", self.as_str())
215 }
216}
217
218impl core::ops::Deref for GlyphName {
219 type Target = str;
220
221 fn deref(&self) -> &Self::Target {
222 self.as_str()
223 }
224}
225
226impl PartialEq<&str> for GlyphName {
227 fn eq(&self, other: &&str) -> bool {
228 self.as_str() == *other
229 }
230}
231
232struct GlyphNameWrite<'a>(&'a mut GlyphName);
233
234impl core::fmt::Write for GlyphNameWrite<'_> {
235 fn write_str(&mut self, s: &str) -> core::fmt::Result {
236 self.0.append(s.as_bytes());
237 Ok(())
238 }
239}
240
241#[derive(Clone)]
242enum Iter<'a> {
243 Post(Range<u32>, Post<'a>),
244 Cff(Cff<'a>, CharsetIter<'a>),
245 Synthesized(Range<u32>),
246}
247
248impl Iter<'_> {
249 fn next_name(&mut self) -> Option<Result<(GlyphId, GlyphName), GlyphId>> {
250 match self {
251 Self::Post(range, post) => {
252 let gid = GlyphId::new(range.next()?);
253 Some(
254 GlyphName::from_post(post, gid)
255 .map(|name| (gid, name))
256 .ok_or(gid),
257 )
258 }
259 Self::Cff(cff, iter) => {
260 let (gid, sid) = iter.next()?;
261 Some(
262 GlyphName::from_cff_sid(cff, sid)
263 .map(|name| (gid, name))
264 .ok_or(gid),
265 )
266 }
267 Self::Synthesized(range) => {
268 let gid = GlyphId::new(range.next()?);
269 Some(Ok((gid, GlyphName::synthesize(gid))))
270 }
271 }
272 }
273}
274
275impl Iterator for Iter<'_> {
276 type Item = (GlyphId, GlyphName);
277
278 fn next(&mut self) -> Option<Self::Item> {
279 match self.next_name()? {
280 Ok(gid_name) => Some(gid_name),
281 Err(gid) => Some((gid, GlyphName::synthesize(gid))),
282 }
283 }
284}
285
286#[cfg(test)]
287mod tests {
288 use super::*;
289
290 #[test]
291 fn synthesized_glyph_names() {
292 let count = 58;
293 let names = GlyphNames {
294 inner: Inner::Synthesized(58),
295 };
296 let names_buf = (0..count).map(|i| format!("gid{i}")).collect::<Vec<_>>();
297 let expected_names = names_buf.iter().map(|s| s.as_str()).collect::<Vec<_>>();
298 for (_, name) in names.iter() {
299 assert!(name.is_synthesized())
300 }
301 check_names(&names, &expected_names, GlyphNameSource::Synthesized);
302 }
303
304 #[test]
305 fn cff_glyph_names() {
306 let font = FontRef::new(font_test_data::NOTO_SERIF_DISPLAY_TRIMMED).unwrap();
307 let names = GlyphNames::new(&font);
308 assert_eq!(names.source(), GlyphNameSource::Cff);
309 let expected_names = [".notdef", "i", "j", "k", "l"];
310 check_names(&names, &expected_names, GlyphNameSource::Cff);
311 }
312
313 #[test]
314 fn post_glyph_names() {
315 let font = FontRef::new(font_test_data::HVAR_WITH_TRUNCATED_ADVANCE_INDEX_MAP).unwrap();
316 let names = GlyphNames::new(&font);
317 let expected_names = [
318 ".notdef",
319 "space",
320 "A",
321 "I",
322 "T",
323 "Aacute",
324 "Agrave",
325 "Iacute",
326 "Igrave",
327 "Amacron",
328 "Imacron",
329 "acutecomb",
330 "gravecomb",
331 "macroncomb",
332 "A.001",
333 "A.002",
334 "A.003",
335 "A.004",
336 "A.005",
337 "A.006",
338 "A.007",
339 "A.008",
340 "A.009",
341 "A.010",
342 ];
343 check_names(&names, &expected_names, GlyphNameSource::Post);
344 }
345
346 #[test]
347 fn post_glyph_names_partial() {
348 let font = FontRef::new(font_test_data::HVAR_WITH_TRUNCATED_ADVANCE_INDEX_MAP).unwrap();
349 let mut names = GlyphNames::new(&font);
350 let Inner::Post(_, len) = &mut names.inner else {
351 panic!("it's a post table!");
352 };
353 *len += 4;
355 let expected_names = [
356 ".notdef",
357 "space",
358 "A",
359 "I",
360 "T",
361 "Aacute",
362 "Agrave",
363 "Iacute",
364 "Igrave",
365 "Amacron",
366 "Imacron",
367 "acutecomb",
368 "gravecomb",
369 "macroncomb",
370 "A.001",
371 "A.002",
372 "A.003",
373 "A.004",
374 "A.005",
375 "A.006",
376 "A.007",
377 "A.008",
378 "A.009",
379 "A.010",
380 "gid24",
382 "gid25",
383 "gid26",
384 "gid27",
385 ];
386 check_names(&names, &expected_names, GlyphNameSource::Post);
387 }
388
389 fn check_names(names: &GlyphNames, expected_names: &[&str], expected_source: GlyphNameSource) {
390 assert_eq!(names.source(), expected_source);
391 let iter_names = names.iter().collect::<Vec<_>>();
392 assert_eq!(iter_names.len(), expected_names.len());
393 for (i, expected) in expected_names.iter().enumerate() {
394 let gid = GlyphId::new(i as u32);
395 let name = names.get(gid).unwrap();
396 assert_eq!(name, expected);
397 assert_eq!(iter_names[i].0, gid);
398 assert_eq!(iter_names[i].1, expected);
399 }
400 }
401}