1include!("../../generated/generated_post.rs");
4
5#[allow(clippy::needless_lifetimes)] impl<'a> Post<'a> {
7 pub fn num_names(&self) -> usize {
9 match self.version() {
10 Version16Dot16::VERSION_1_0 => DEFAULT_GLYPH_NAMES.len(),
11 Version16Dot16::VERSION_2_0 => self.num_glyphs().unwrap_or_default() as usize,
12 _ => 0,
13 }
14 }
15
16 pub fn glyph_name(&self, glyph_id: GlyphId16) -> Option<&'a str> {
23 let glyph_id = glyph_id.to_u16() as usize;
24 match self.version() {
25 Version16Dot16::VERSION_1_0 => DEFAULT_GLYPH_NAMES.get(glyph_id).copied(),
26 Version16Dot16::VERSION_2_0 => {
27 let idx = self.glyph_name_index()?.get(glyph_id)?.get() as usize;
28 if idx < DEFAULT_GLYPH_NAMES.len() {
29 return DEFAULT_GLYPH_NAMES.get(idx).copied();
30 }
31 let idx = idx - DEFAULT_GLYPH_NAMES.len();
32 self.string_data()?.get(idx)?.ok().map(|s| s.0)
33 }
34 _ => None,
35 }
36 }
37
38 pub fn glyph_names(&self) -> GlyphNames<'a> {
40 let num_names = self.num_names() as u32;
41 let kind = match self.version() {
42 Version16Dot16::VERSION_1_0 => GlyphNameIterKind::V1(self.clone(), 0),
43 Version16Dot16::VERSION_2_0 => GlyphNameIterKind::V2 {
44 post: self.clone(),
45 idx: 0,
46 checkpoint_stride: (num_names as usize).div_ceil(NUM_CHECKPOINTS + 1).max(1),
47 checkpoints: [UNSET_CHECKPOINT; NUM_CHECKPOINTS],
48 last_actual_idx: None,
49 last_offset: 0,
50 },
51 _ => GlyphNameIterKind::None,
52 };
53 GlyphNames { num_names, kind }
54 }
55
56 #[cfg(feature = "experimental_traverse")]
59 fn traverse_string_data(&self) -> FieldType<'a> {
60 FieldType::I8(-42) }
62}
63
64#[derive(Debug, Clone, Copy, PartialEq, Eq)]
69pub struct PString<'a>(&'a str);
70
71impl<'a> PString<'a> {
72 pub fn as_str(&self) -> &'a str {
73 self.0
74 }
75}
76
77impl std::ops::Deref for PString<'_> {
78 type Target = str;
79 fn deref(&self) -> &Self::Target {
80 self.0
81 }
82}
83
84impl PartialEq<&str> for PString<'_> {
85 fn eq(&self, other: &&str) -> bool {
86 self.0 == *other
87 }
88}
89
90impl ReadArgs for PString<'_> {
91 type Args = ();
92}
93
94impl<'a> FontRead<'a> for PString<'a> {
95 fn read_with_args(data: FontData<'a>, _: ()) -> Result<Self, ReadError> {
96 let len: u8 = data.read_at(0)?;
97 let pstring = data
98 .as_bytes()
99 .get(1..len as usize + 1)
100 .ok_or(ReadError::OutOfBounds)?;
101
102 if pstring.is_ascii() {
103 Ok(PString(std::str::from_utf8(pstring).unwrap()))
104 } else {
105 Err(ReadError::MalformedData("Must be valid ascii"))
107 }
108 }
109}
110
111impl VarSize for PString<'_> {
112 type Size = u8;
113}
114
115const NUM_CHECKPOINTS: usize = 16;
116const UNSET_CHECKPOINT: u32 = u32::MAX;
117
118#[derive(Clone)]
120pub struct GlyphNames<'a> {
121 num_names: u32,
122 kind: GlyphNameIterKind<'a>,
123}
124
125#[derive(Clone)]
126enum GlyphNameIterKind<'a> {
127 None,
128 V1(Post<'a>, u32),
129 V2 {
130 post: Post<'a>,
131 idx: u32,
132 checkpoint_stride: usize,
134 checkpoints: [u32; NUM_CHECKPOINTS],
136 last_actual_idx: Option<usize>,
138 last_offset: usize,
140 },
141}
142
143impl<'a> Iterator for GlyphNames<'a> {
144 type Item = (GlyphId, &'a str);
145
146 fn next(&mut self) -> Option<Self::Item> {
147 match &mut self.kind {
148 GlyphNameIterKind::None => None,
149 GlyphNameIterKind::V1(post, idx) => {
150 if *idx >= self.num_names {
151 return None;
152 }
153 let gid = GlyphId16::new(*idx as u16);
154 let name = post.glyph_name(gid)?;
155 *idx += 1;
156 Some((gid.into(), name))
157 }
158 GlyphNameIterKind::V2 {
159 post,
160 idx,
161 checkpoint_stride,
162 checkpoints,
163 last_actual_idx,
164 last_offset,
165 } => {
166 if *idx >= self.num_names {
167 return None;
168 }
169 let stride = *checkpoint_stride;
170 let gid = GlyphId16::new(*idx as u16);
171 let mut actual_idx = post.glyph_name_index()?.get(*idx as usize)?.get() as usize;
172 let name = if actual_idx < DEFAULT_GLYPH_NAMES.len() {
173 DEFAULT_GLYPH_NAMES.get(actual_idx).copied()?
174 } else {
175 actual_idx -= DEFAULT_GLYPH_NAMES.len();
176 let string_data = post.data.slice(post.string_data_byte_range())?;
177 let target_slot = (actual_idx / stride).min(NUM_CHECKPOINTS);
180 let (mut scan_idx, mut offset) = {
182 let mut slot = target_slot;
185 while slot > 0 && checkpoints[slot - 1] == UNSET_CHECKPOINT {
186 slot -= 1;
187 }
188 if slot == 0 {
189 (0, 0)
191 } else {
192 (slot * stride, checkpoints[slot - 1] as usize)
195 }
196 };
197 if let Some(last_idx) = *last_actual_idx {
199 if last_idx <= actual_idx && last_idx > scan_idx {
202 scan_idx = last_idx;
203 offset = *last_offset;
204 }
205 }
206 while scan_idx < actual_idx {
208 let item_len = PString::read_len_at(string_data, offset)?;
209 offset = offset.checked_add(item_len)?;
210 scan_idx += 1;
211 if scan_idx % stride == 0 {
214 let slot = (scan_idx / stride).min(NUM_CHECKPOINTS);
215 if slot > 0 {
216 checkpoints[slot - 1] = u32::try_from(offset).ok()?;
217 }
218 }
219 }
220 if actual_idx % stride == 0 && target_slot > 0 {
221 checkpoints[target_slot - 1] = u32::try_from(offset).ok()?;
222 }
223 *last_actual_idx = Some(actual_idx);
225 *last_offset = offset;
226 PString::read(string_data.split_off(offset)?).ok()?.0
227 };
228 *idx += 1;
229 Some((gid.into(), name))
230 }
231 }
232 }
233}
234
235#[rustfmt::skip]
237pub static DEFAULT_GLYPH_NAMES: [&str; 258] = [
238 ".notdef", ".null", "nonmarkingreturn", "space", "exclam", "quotedbl", "numbersign", "dollar",
239 "percent", "ampersand", "quotesingle", "parenleft", "parenright", "asterisk", "plus", "comma",
240 "hyphen", "period", "slash", "zero", "one", "two", "three", "four", "five", "six", "seven",
241 "eight", "nine", "colon", "semicolon", "less", "equal", "greater", "question", "at", "A", "B",
242 "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U",
243 "V", "W", "X", "Y", "Z", "bracketleft", "backslash", "bracketright", "asciicircum",
244 "underscore", "grave", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n",
245 "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "braceleft", "bar", "braceright",
246 "asciitilde", "Adieresis", "Aring", "Ccedilla", "Eacute", "Ntilde", "Odieresis", "Udieresis",
247 "aacute", "agrave", "acircumflex", "adieresis", "atilde", "aring", "ccedilla", "eacute",
248 "egrave", "ecircumflex", "edieresis", "iacute", "igrave", "icircumflex", "idieresis", "ntilde",
249 "oacute", "ograve", "ocircumflex", "odieresis", "otilde", "uacute", "ugrave", "ucircumflex",
250 "udieresis", "dagger", "degree", "cent", "sterling", "section", "bullet", "paragraph",
251 "germandbls", "registered", "copyright", "trademark", "acute", "dieresis", "notequal", "AE",
252 "Oslash", "infinity", "plusminus", "lessequal", "greaterequal", "yen", "mu", "partialdiff",
253 "summation", "product", "pi", "integral", "ordfeminine", "ordmasculine", "Omega", "ae",
254 "oslash", "questiondown", "exclamdown", "logicalnot", "radical", "florin", "approxequal",
255 "Delta", "guillemotleft", "guillemotright", "ellipsis", "nonbreakingspace", "Agrave", "Atilde",
256 "Otilde", "OE", "oe", "endash", "emdash", "quotedblleft", "quotedblright", "quoteleft",
257 "quoteright", "divide", "lozenge", "ydieresis", "Ydieresis", "fraction", "currency",
258 "guilsinglleft", "guilsinglright", "fi", "fl", "daggerdbl", "periodcentered", "quotesinglbase",
259 "quotedblbase", "perthousand", "Acircumflex", "Ecircumflex", "Aacute", "Edieresis", "Egrave",
260 "Iacute", "Icircumflex", "Idieresis", "Igrave", "Oacute", "Ocircumflex", "apple", "Ograve",
261 "Uacute", "Ucircumflex", "Ugrave", "dotlessi", "circumflex", "tilde", "macron", "breve",
262 "dotaccent", "ring", "cedilla", "hungarumlaut", "ogonek", "caron", "Lslash", "lslash",
263 "Scaron", "scaron", "Zcaron", "zcaron", "brokenbar", "Eth", "eth", "Yacute", "yacute", "Thorn",
264 "thorn", "minus", "multiply", "onesuperior", "twosuperior", "threesuperior", "onehalf",
265 "onequarter", "threequarters", "franc", "Gbreve", "gbreve", "Idotaccent", "Scedilla",
266 "scedilla", "Cacute", "cacute", "Ccaron", "ccaron", "dcroat",
267];
268
269#[cfg(test)]
270mod tests {
271 use super::*;
272 use font_test_data::{bebuffer::BeBuffer, post as test_data};
273
274 #[test]
275 fn test_post() {
276 let table = Post::read(test_data::SIMPLE.into()).unwrap();
277 assert_eq!(table.version(), Version16Dot16::VERSION_2_0);
278 assert_eq!(table.underline_position(), FWord::new(-75));
279 assert_eq!(table.glyph_name(GlyphId16::new(1)), Some(".notdef"));
280 assert_eq!(table.glyph_name(GlyphId16::new(2)), Some("space"));
281 assert_eq!(table.glyph_name(GlyphId16::new(7)), Some("hello"));
282 assert_eq!(table.glyph_name(GlyphId16::new(8)), Some("hi"));
283 assert_eq!(table.glyph_name(GlyphId16::new(9)), Some("hola"));
284 }
285
286 fn make_basic_post(version: Version16Dot16, include_num_glyphs: bool) -> BeBuffer {
287 let buf = BeBuffer::new()
288 .push(version)
289 .push(Fixed::from_i32(5))
290 .extend([FWord::new(6), FWord::new(7)]) .push(0u32) .extend([7u32, 8, 9, 10]); if include_num_glyphs {
294 buf.push(0u16)
295 } else {
296 buf
297 }
298 }
299
300 #[test]
301 fn parse_versioned_fields_v1() {
302 let buf = make_basic_post(Version16Dot16::VERSION_1_0, true);
305 let postv1 = Post::read(buf.data().into()).unwrap();
306 assert!(postv1.num_glyphs().is_none());
307 }
308
309 #[test]
310 fn parse_versioned_fields_v2() {
311 let buf = make_basic_post(Version16Dot16::VERSION_2_0, false);
312 let postv2 = Post::read(buf.data().into()).unwrap();
313 assert!(postv2.num_glyphs().is_none());
315
316 let buf = make_basic_post(Version16Dot16::VERSION_2_0, true);
318 let postv2 = Post::read(buf.data().into()).unwrap();
319 assert_eq!(postv2.num_glyphs(), Some(0));
321 }
322
323 #[test]
324 fn parse_versioned_fields_v3() {
325 let buf = make_basic_post(Version16Dot16::VERSION_3_0, true);
327 let postv3 = Post::read(buf.data().into()).unwrap();
328 assert!(postv3.num_glyphs().is_none());
329 }
330
331 #[test]
332 fn num_names_defaults_to_zero_without_num_glyphs() {
333 let buf = make_basic_post(Version16Dot16::VERSION_2_0, false);
334 let post = Post::read(buf.data().into()).unwrap();
335 assert_eq!(post.num_names(), 0);
337 }
338
339 #[test]
340 fn glyph_name_missing_string_data_returns_none() {
341 let buf = BeBuffer::new()
342 .push(Version16Dot16::VERSION_2_0)
343 .push(Fixed::from_i32(5))
344 .extend([FWord::new(6), FWord::new(7)])
345 .push(0u32)
346 .extend([7u32, 8, 9, 10])
347 .push(1u16)
348 .push(258u16);
349 let post = Post::read(buf.data().into()).unwrap();
350 assert_eq!(post.glyph_name(GlyphId16::new(0)), None);
352 }
353
354 #[test]
355 fn glyph_names_matches_naive_on_varied_synthetic_v2_data() {
356 let num_glyphs = 2_000u16;
357 let orders = [
358 test_data::GlyphNameOrder::Monotonic,
359 test_data::GlyphNameOrder::MostlyMonotonicWithBackrefs,
360 test_data::GlyphNameOrder::AllPointToLast,
361 ];
362 for order in orders {
363 let (bytes, expected_custom_names) =
364 test_data::v2_with_varied_glyph_names(num_glyphs, 63, order);
365 let post = Post::read(bytes.as_slice().into()).unwrap();
366 let from_naive: Vec<_> = (0..num_glyphs)
367 .map(|gid| post.glyph_name(GlyphId16::new(gid)).unwrap())
368 .collect();
369 let from_iter: Vec<_> = post.glyph_names().map(|(_, name)| name).collect();
370 assert_eq!(from_iter, from_naive);
371 for (name, expected) in from_iter.iter().zip(expected_custom_names.iter()) {
372 assert_eq!(*name, expected.as_str());
373 }
374 }
375 }
376}