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() as usize,
12 _ => 0,
13 }
14 }
15
16 pub fn glyph_name(&self, glyph_id: GlyphId16) -> Option<&str> {
17 let glyph_id = glyph_id.to_u16() as usize;
18 match self.version() {
19 Version16Dot16::VERSION_1_0 => DEFAULT_GLYPH_NAMES.get(glyph_id).copied(),
20 Version16Dot16::VERSION_2_0 => {
21 let idx = self.glyph_name_index()?.get(glyph_id)?.get() as usize;
22 if idx < DEFAULT_GLYPH_NAMES.len() {
23 return DEFAULT_GLYPH_NAMES.get(idx).copied();
24 }
25 let idx = idx - DEFAULT_GLYPH_NAMES.len();
26 match self.string_data().unwrap().get(idx) {
27 Some(Ok(s)) => Some(s.0),
28 _ => None,
29 }
30 }
31 _ => None,
32 }
33 }
34
35 #[cfg(feature = "experimental_traverse")]
38 fn traverse_string_data(&self) -> FieldType<'a> {
39 FieldType::I8(-42) }
41}
42
43#[derive(Debug, Clone, Copy, PartialEq, Eq)]
48pub struct PString<'a>(&'a str);
49
50impl<'a> PString<'a> {
51 pub fn as_str(&self) -> &'a str {
52 self.0
53 }
54}
55
56impl std::ops::Deref for PString<'_> {
57 type Target = str;
58 fn deref(&self) -> &Self::Target {
59 self.0
60 }
61}
62
63impl PartialEq<&str> for PString<'_> {
64 fn eq(&self, other: &&str) -> bool {
65 self.0 == *other
66 }
67}
68
69impl<'a> FontRead<'a> for PString<'a> {
70 fn read(data: FontData<'a>) -> Result<Self, ReadError> {
71 let len: u8 = data.read_at(0)?;
72 let pstring = data
73 .as_bytes()
74 .get(1..len as usize + 1)
75 .ok_or(ReadError::OutOfBounds)?;
76
77 if pstring.is_ascii() {
78 Ok(PString(std::str::from_utf8(pstring).unwrap()))
79 } else {
80 Err(ReadError::MalformedData("Must be valid ascii"))
82 }
83 }
84}
85
86impl VarSize for PString<'_> {
87 type Size = u8;
88}
89
90#[rustfmt::skip]
92pub static DEFAULT_GLYPH_NAMES: [&str; 258] = [
93 ".notdef", ".null", "nonmarkingreturn", "space", "exclam", "quotedbl", "numbersign", "dollar",
94 "percent", "ampersand", "quotesingle", "parenleft", "parenright", "asterisk", "plus", "comma",
95 "hyphen", "period", "slash", "zero", "one", "two", "three", "four", "five", "six", "seven",
96 "eight", "nine", "colon", "semicolon", "less", "equal", "greater", "question", "at", "A", "B",
97 "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U",
98 "V", "W", "X", "Y", "Z", "bracketleft", "backslash", "bracketright", "asciicircum",
99 "underscore", "grave", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n",
100 "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "braceleft", "bar", "braceright",
101 "asciitilde", "Adieresis", "Aring", "Ccedilla", "Eacute", "Ntilde", "Odieresis", "Udieresis",
102 "aacute", "agrave", "acircumflex", "adieresis", "atilde", "aring", "ccedilla", "eacute",
103 "egrave", "ecircumflex", "edieresis", "iacute", "igrave", "icircumflex", "idieresis", "ntilde",
104 "oacute", "ograve", "ocircumflex", "odieresis", "otilde", "uacute", "ugrave", "ucircumflex",
105 "udieresis", "dagger", "degree", "cent", "sterling", "section", "bullet", "paragraph",
106 "germandbls", "registered", "copyright", "trademark", "acute", "dieresis", "notequal", "AE",
107 "Oslash", "infinity", "plusminus", "lessequal", "greaterequal", "yen", "mu", "partialdiff",
108 "summation", "product", "pi", "integral", "ordfeminine", "ordmasculine", "Omega", "ae",
109 "oslash", "questiondown", "exclamdown", "logicalnot", "radical", "florin", "approxequal",
110 "Delta", "guillemotleft", "guillemotright", "ellipsis", "nonbreakingspace", "Agrave", "Atilde",
111 "Otilde", "OE", "oe", "endash", "emdash", "quotedblleft", "quotedblright", "quoteleft",
112 "quoteright", "divide", "lozenge", "ydieresis", "Ydieresis", "fraction", "currency",
113 "guilsinglleft", "guilsinglright", "fi", "fl", "daggerdbl", "periodcentered", "quotesinglbase",
114 "quotedblbase", "perthousand", "Acircumflex", "Ecircumflex", "Aacute", "Edieresis", "Egrave",
115 "Iacute", "Icircumflex", "Idieresis", "Igrave", "Oacute", "Ocircumflex", "apple", "Ograve",
116 "Uacute", "Ucircumflex", "Ugrave", "dotlessi", "circumflex", "tilde", "macron", "breve",
117 "dotaccent", "ring", "cedilla", "hungarumlaut", "ogonek", "caron", "Lslash", "lslash",
118 "Scaron", "scaron", "Zcaron", "zcaron", "brokenbar", "Eth", "eth", "Yacute", "yacute", "Thorn",
119 "thorn", "minus", "multiply", "onesuperior", "twosuperior", "threesuperior", "onehalf",
120 "onequarter", "threequarters", "franc", "Gbreve", "gbreve", "Idotaccent", "Scedilla",
121 "scedilla", "Cacute", "cacute", "Ccaron", "ccaron", "dcroat",
122];
123
124#[cfg(test)]
125mod tests {
126 use super::*;
127 use font_test_data::{bebuffer::BeBuffer, post as test_data};
128
129 #[test]
130 fn test_post() {
131 let table = Post::read(test_data::SIMPLE.into()).unwrap();
132 assert_eq!(table.version(), Version16Dot16::VERSION_2_0);
133 assert_eq!(table.underline_position(), FWord::new(-75));
134 assert_eq!(table.glyph_name(GlyphId16::new(1)), Some(".notdef"));
135 assert_eq!(table.glyph_name(GlyphId16::new(2)), Some("space"));
136 assert_eq!(table.glyph_name(GlyphId16::new(7)), Some("hello"));
137 assert_eq!(table.glyph_name(GlyphId16::new(8)), Some("hi"));
138 assert_eq!(table.glyph_name(GlyphId16::new(9)), Some("hola"));
139 }
140
141 fn make_basic_post(version: Version16Dot16, include_num_glyphs: bool) -> BeBuffer {
142 let buf = BeBuffer::new()
143 .push(version)
144 .push(Fixed::from_i32(5))
145 .extend([FWord::new(6), FWord::new(7)]) .push(0u32) .extend([7u32, 8, 9, 10]); if include_num_glyphs {
149 buf.push(0u16)
150 } else {
151 buf
152 }
153 }
154
155 #[test]
156 fn parse_versioned_fields_v1() {
157 let buf = make_basic_post(Version16Dot16::VERSION_1_0, true);
160 let postv1 = Post::read(buf.data().into()).unwrap();
161 assert!(postv1.num_glyphs().is_none());
162 }
163
164 #[test]
165 fn parse_versioned_fields_v2() {
166 let buf = make_basic_post(Version16Dot16::VERSION_2_0, false);
167 let postv2 = Post::read(buf.data().into()).unwrap();
168 assert!(postv2.num_glyphs().is_none());
170
171 let buf = make_basic_post(Version16Dot16::VERSION_2_0, true);
173 let postv2 = Post::read(buf.data().into()).unwrap();
174 assert_eq!(postv2.num_glyphs(), Some(0));
176 }
177
178 #[test]
179 fn parse_versioned_fields_v3() {
180 let buf = make_basic_post(Version16Dot16::VERSION_3_0, true);
182 let postv3 = Post::read(buf.data().into()).unwrap();
183 assert!(postv3.num_glyphs().is_none());
184 }
185}