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 #[test]
142 fn parse_versioned_fields() {
143 fn make_basic_post(version: Version16Dot16) -> BeBuffer {
144 BeBuffer::new()
145 .push(version)
146 .push(Fixed::from_i32(5))
147 .extend([FWord::new(6), FWord::new(7)]) .push(0u32) .extend([7u32, 8, 9, 10]) }
151
152 let buf = make_basic_post(Version16Dot16::VERSION_2_0);
154 assert!(Post::read(buf.data().into()).is_err());
155
156 let buf = make_basic_post(Version16Dot16::VERSION_3_0);
158 assert!(Post::read(buf.data().into()).is_ok());
159 }
160}