1use nom::{
2 bytes::complete::{tag, take, take_until},
3 character::complete::{multispace0, space0},
4 combinator::{eof, map, map_parser, map_res, opt},
5 multi::many0,
6 sequence::{delimited, preceded, terminated},
7 IResult,
8};
9use std::convert::TryFrom;
10
11use crate::{helpers::*, BoundingBox, Coord};
12
13#[derive(Debug, Clone, PartialEq)]
15pub struct Glyph {
16 pub name: String,
18
19 pub encoding: Option<char>,
21
22 pub scalable_width: Option<Coord>,
24
25 pub device_width: Coord,
27
28 pub bounding_box: BoundingBox,
30
31 pub bitmap: Vec<u8>,
33}
34
35impl Glyph {
36 fn parse(input: &[u8]) -> IResult<&[u8], Self> {
37 let (input, name) = statement("STARTCHAR", parse_string)(input)?;
38 let (input, encoding) = statement("ENCODING", parse_encoding)(input)?;
39 let (input, scalable_width) = opt(statement("SWIDTH", Coord::parse))(input)?;
40 let (input, device_width) = statement("DWIDTH", Coord::parse)(input)?;
41 let (input, bounding_box) = statement("BBX", BoundingBox::parse)(input)?;
42 let (input, bitmap) = parse_bitmap(input)?;
43
44 Ok((
45 input,
46 Self {
47 name,
48 encoding,
49 scalable_width,
50 device_width,
51 bounding_box,
52 bitmap,
53 },
54 ))
55 }
56
57 pub fn pixel(&self, x: usize, y: usize) -> bool {
67 let width = usize::try_from(self.bounding_box.size.x).unwrap();
68
69 let bytes_per_row = (width + 7) / 8;
70 let byte_offset = x / 8;
71 let bit_mask = 0x80 >> (x % 8);
72
73 self.bitmap[byte_offset + bytes_per_row * y] & bit_mask != 0
74 }
75}
76
77fn parse_encoding(input: &[u8]) -> IResult<&[u8], Option<char>> {
78 map(parse_to_i32, |code| {
79 u32::try_from(code).ok().and_then(std::char::from_u32)
80 })(input)
81}
82
83fn parse_bitmap(input: &[u8]) -> IResult<&[u8], Vec<u8>> {
84 map_parser(
85 delimited(
86 statement("BITMAP", eof),
87 take_until("ENDCHAR"),
88 statement("ENDCHAR", eof),
89 ),
90 preceded(multispace0, many0(terminated(parse_hex_byte, multispace0))),
91 )(input)
92}
93
94fn parse_hex_byte(input: &[u8]) -> IResult<&[u8], u8> {
95 map_res(
96 map_res(take(2usize), |bytes| std::str::from_utf8(bytes)),
97 |v| u8::from_str_radix(v, 16),
98 )(input)
99}
100
101#[derive(Debug, Clone, PartialEq)]
103pub struct Glyphs {
104 glyphs: Vec<Glyph>,
105}
106
107impl Glyphs {
108 pub(crate) fn parse(input: &[u8]) -> IResult<&[u8], Self> {
109 map(
110 preceded(
111 terminated(opt(numchars), multispace0),
112 many0(terminated(Glyph::parse, multispace0)),
113 ),
114 |glyphs| Self { glyphs },
115 )(input)
116 }
117
118 pub fn get(&self, c: char) -> Option<&Glyph> {
120 self.glyphs.iter().find(|glyph| glyph.encoding == Some(c))
121 }
122
123 pub fn iter(&self) -> impl Iterator<Item = &Glyph> {
125 self.glyphs.iter()
126 }
127}
128
129fn numchars(input: &[u8]) -> IResult<&[u8], u32> {
130 preceded(
131 space0,
132 preceded(tag("CHARS"), preceded(space0, parse_to_u32)),
133 )(input)
134}
135
136#[cfg(test)]
137mod tests {
138 use super::*;
139 use indoc::indoc;
140
141 #[test]
142 fn test_parse_bitmap() {
143 assert_parser_ok!(parse_bitmap(b"BITMAP\n7e\nENDCHAR"), vec![0x7e]);
144 assert_parser_ok!(parse_bitmap(b"BITMAP\nff\nENDCHAR"), vec![0xff]);
145 assert_parser_ok!(parse_bitmap(b"BITMAP\nCCCC\nENDCHAR"), vec![0xcc, 0xcc]);
146 assert_parser_ok!(
147 parse_bitmap(b"BITMAP\nffffffff\nENDCHAR"),
148 vec![0xff, 0xff, 0xff, 0xff]
149 );
150 assert_parser_ok!(
151 parse_bitmap(b"BITMAP\nffffffff\naaaaaaaa\nENDCHAR"),
152 vec![0xff, 0xff, 0xff, 0xff, 0xaa, 0xaa, 0xaa, 0xaa]
153 );
154 assert_parser_ok!(
155 parse_bitmap(b"BITMAP\nff\nff\nff\nff\naa\naa\naa\naa\nENDCHAR"),
156 vec![0xff, 0xff, 0xff, 0xff, 0xaa, 0xaa, 0xaa, 0xaa]
157 );
158 assert_parser_ok!(
159 parse_bitmap(
160 b"BITMAP\n00\n00\n00\n00\n18\n24\n24\n42\n42\n7E\n42\n42\n42\n42\n00\n00\nENDCHAR"
161 ),
162 vec![
163 0x00, 0x00, 0x00, 0x00, 0x18, 0x24, 0x24, 0x42, 0x42, 0x7e, 0x42, 0x42, 0x42, 0x42,
164 0x00, 0x00
165 ]
166 );
167 }
168
169 fn test_data() -> (&'static [u8], Glyph) {
171 (
172 indoc! {br#"
173 STARTCHAR ZZZZ
174 ENCODING 65
175 SWIDTH 500 0
176 DWIDTH 8 0
177 BBX 8 16 0 -2
178 BITMAP
179 00
180 00
181 00
182 00
183 18
184 24
185 24
186 42
187 42
188 7E
189 42
190 42
191 42
192 42
193 00
194 00
195 ENDCHAR
196 "#},
197 Glyph {
198 name: "ZZZZ".to_string(),
199 encoding: Some('A'), bitmap: vec![
201 0x00, 0x00, 0x00, 0x00, 0x18, 0x24, 0x24, 0x42, 0x42, 0x7e, 0x42, 0x42, 0x42,
202 0x42, 0x00, 0x00,
203 ],
204 bounding_box: BoundingBox {
205 size: Coord::new(8, 16),
206 offset: Coord::new(0, -2),
207 },
208 scalable_width: Some(Coord::new(500, 0)),
209 device_width: Coord::new(8, 0),
210 },
211 )
212 }
213
214 #[test]
215 fn parse_single_char() {
216 let (chardata, expected_glyph) = test_data();
217
218 assert_parser_ok!(Glyph::parse(chardata), expected_glyph.clone());
219 }
220
221 #[test]
222 fn get_glyph_by_char() {
223 let (chardata, expected_glyph) = test_data();
224
225 let (input, glyphs) = Glyphs::parse(chardata).unwrap();
226 assert!(input.is_empty());
227 assert_eq!(glyphs.get('A'), Some(&expected_glyph));
228 }
229
230 #[test]
231 fn access_pixels() {
232 let (chardata, _) = test_data();
233 let (input, glyph) = Glyph::parse(chardata).unwrap();
234 assert!(input.is_empty());
235
236 let bitmap = (0..16)
237 .map(|y| {
238 (0..8)
239 .map(|x| if glyph.pixel(x, y) { '#' } else { ' ' })
240 .collect::<String>()
241 })
242 .collect::<Vec<_>>();
243
244 assert_eq!(
245 bitmap,
246 [
247 " ", " ", " ", " ", " ## ", " # # ", " # # ", " # # ", " # # ", " ###### ", " # # ", " # # ", " # # ", " # # ", " ", " ", ]
264 .iter()
265 .map(|s| s.to_string())
266 .collect::<Vec<_>>()
267 );
268 }
269
270 #[test]
271 fn parse_glyph_with_no_encoding() {
272 let chardata = indoc! {br#"
273 STARTCHAR 000
274 ENCODING -1
275 SWIDTH 432 0
276 DWIDTH 6 0
277 BBX 0 0 0 0
278 BITMAP
279 ENDCHAR
280 "#};
281
282 assert_parser_ok!(
283 Glyph::parse(chardata),
284 Glyph {
285 bitmap: vec![],
286 bounding_box: BoundingBox {
287 size: Coord::new(0, 0),
288 offset: Coord::new(0, 0),
289 },
290 encoding: None,
291 name: "000".to_string(),
292 scalable_width: Some(Coord::new(432, 0)),
293 device_width: Coord::new(6, 0),
294 }
295 );
296 }
297
298 #[test]
299 fn parse_glyph_with_empty_bitmap() {
300 let chardata = indoc! {br#"
301 STARTCHAR 000
302 ENCODING 0
303 SWIDTH 432 0
304 DWIDTH 6 0
305 BBX 0 0 0 0
306 BITMAP
307 ENDCHAR
308 "#};
309
310 assert_parser_ok!(
311 Glyph::parse(chardata),
312 Glyph {
313 bitmap: vec![],
314 bounding_box: BoundingBox {
315 size: Coord::new(0, 0),
316 offset: Coord::new(0, 0),
317 },
318 encoding: Some('\x00'),
319 name: "000".to_string(),
320 scalable_width: Some(Coord::new(432, 0)),
321 device_width: Coord::new(6, 0),
322 }
323 );
324 }
325}