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