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