1use std::convert::TryFrom;
2
3use crate::{
4 parser::{Line, Lines},
5 BoundingBox, Coord, Metadata, ParserError,
6};
7
8#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
10pub enum Encoding {
11 Standard(u32),
13 NonStandard(u32),
15 #[default]
17 Unspecified,
18}
19
20#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
22pub struct GlyphWidth {
23 pub scalable: Coord,
25 pub device: Coord,
27}
28
29#[derive(Debug, Clone, PartialEq, Default)]
31pub struct Glyph {
32 pub name: String,
36
37 pub encoding: Encoding,
41
42 pub width_horizontal: Option<GlyphWidth>,
47
48 pub width_vertical: Option<GlyphWidth>,
53
54 pub bounding_box: BoundingBox,
58
59 pub origin_offset: Option<Coord>,
63
64 pub bitmap: Vec<u8>,
68}
69
70fn parse_bitmap_row(line: &Line<'_>, bitmap: &mut Vec<u8>) -> Result<(), ()> {
71 if !line.parameters.is_empty() || line.keyword.len() % 2 != 0 {
72 return Err(());
73 }
74
75 for hex in line.keyword.as_bytes().chunks_exact(2) {
78 let byte = str::from_utf8(hex)
79 .ok()
80 .and_then(|s| u8::from_str_radix(s, 16).ok())
81 .ok_or(())?;
82 bitmap.push(byte);
83 }
84
85 Ok(())
86}
87
88fn calculate_swidth(device_width: Coord, metadata: &Metadata) -> Coord {
90 Coord {
91 x: device_width.x * 1000 * 72 / metadata.point_size / metadata.resolution.x,
92 y: device_width.y * 1000 * 72 / metadata.point_size / metadata.resolution.y,
93 }
94}
95
96impl Glyph {
97 pub(crate) fn parse(
98 mut lines: &mut Lines<'_>,
99 metadata: &Metadata,
100 ) -> Result<Self, crate::ParserError> {
101 let mut encoding = Encoding::Unspecified;
102 let mut swidth = None;
103 let mut dwidth = None;
104 let mut swidth1 = None;
105 let mut dwidth1 = None;
106 let mut bbx = BoundingBox {
107 size: Coord::new(0, 0),
108 offset: Coord::new(0, 0),
109 };
110 let mut vvector = None;
111
112 let start = lines.next().unwrap();
113 assert_eq!(start.keyword, "STARTCHAR");
114 let name = start.parameters;
115
116 for line in &mut lines {
117 match line.keyword {
118 "ENCODING" => {
119 encoding = if let Some([index1, index2]) = line.parse_integer_parameters() {
120 if index1 >= 0 || index2 < 0 {
121 return Err(ParserError::with_line("invalid \"ENCODING\"", &line));
122 }
123
124 Encoding::NonStandard(index2 as u32)
125 } else if let Some([index]) = line.parse_integer_parameters() {
126 if index >= 0 {
127 Encoding::Standard(index as u32)
128 } else {
129 Encoding::Unspecified
130 }
131 } else {
132 return Err(ParserError::with_line("invalid \"ENCODING\"", &line));
133 };
134 }
135 "SWIDTH" => {
136 swidth = Some(
137 Coord::parse(&line)
138 .ok_or_else(|| ParserError::with_line("invalid \"SWIDTH\"", &line))?,
139 );
140 }
141 "DWIDTH" => {
142 dwidth = Some(
143 Coord::parse(&line)
144 .ok_or_else(|| ParserError::with_line("invalid \"DWIDTH\"", &line))?,
145 );
146 }
147 "SWIDTH1" => {
148 swidth1 = Some(
149 Coord::parse(&line)
150 .ok_or_else(|| ParserError::with_line("invalid \"SWIDTH1\"", &line))?,
151 );
152 }
153 "DWIDTH1" => {
154 dwidth1 = Some(
155 Coord::parse(&line)
156 .ok_or_else(|| ParserError::with_line("invalid \"DWIDTH1\"", &line))?,
157 );
158 }
159 "BBX" => {
160 bbx = BoundingBox::parse(&line)
161 .ok_or_else(|| ParserError::with_line("invalid \"BBX\"", &line))?;
162 }
163 "VVECTOR" => {
164 vvector = Some(
165 Coord::parse(&line)
166 .ok_or_else(|| ParserError::with_line("invalid \"VVECTOR\"", &line))?,
167 );
168 }
169 "BITMAP" => {
170 break;
171 }
172 _ => {
173 return Err(ParserError::with_line(
174 &format!("unknown keyword in glyphs: \"{}\"", line.keyword),
175 &line,
176 ))
177 }
178 }
179 }
180
181 let mut bitmap = Vec::new();
182 for line in &mut lines {
183 if line.keyword == "ENDCHAR" {
184 break;
185 }
186
187 parse_bitmap_row(&line, &mut bitmap)
188 .map_err(|_| ParserError::with_line("invalid hex data in BITMAP", &line))?;
189 }
190
191 let width_horizontal = if swidth.is_some() || dwidth.is_some() {
192 let device =
193 dwidth.ok_or_else(|| ParserError::with_line("missing \"DWIDTH\"", &start))?;
194
195 let scalable = swidth.unwrap_or_else(|| calculate_swidth(device, metadata));
199
200 Some(GlyphWidth { scalable, device })
201 } else {
202 None
203 };
204
205 let width_vertical = if swidth1.is_some() || dwidth1.is_some() {
206 let device =
207 dwidth1.ok_or_else(|| ParserError::with_line("missing \"DWIDTH1\"", &start))?;
208
209 let scalable = swidth1.unwrap_or_else(|| calculate_swidth(device, metadata));
213
214 Some(GlyphWidth { scalable, device })
215 } else {
216 None
217 };
218
219 Ok(Self {
220 name: name.to_string(),
221 encoding,
222 width_horizontal,
223 width_vertical,
224 bounding_box: bbx,
225 bitmap,
226 origin_offset: vvector,
227 })
228 }
229
230 pub fn pixel(&self, x: usize, y: usize) -> Option<bool> {
238 let width = usize::try_from(self.bounding_box.size.x).unwrap();
239
240 if x >= width {
241 return None;
242 }
243
244 let bytes_per_row = width.div_ceil(8);
245 let byte_offset = x / 8;
246 let bit_mask = 0x80 >> (x % 8);
247
248 self.bitmap
249 .get(byte_offset + bytes_per_row * y)
250 .map(|v| v & bit_mask != 0)
251 }
252
253 pub fn pixels(&self) -> impl Iterator<Item = bool> + '_ {
258 let width = usize::try_from(self.bounding_box.size.x).unwrap();
259 let height = usize::try_from(self.bounding_box.size.y).unwrap();
260
261 (0..height).flat_map(move |y| (0..width).map(move |x| self.pixel(x, y).unwrap()))
262 }
263}
264
265#[derive(Debug, Clone, PartialEq)]
267pub struct Glyphs {
268 glyphs: Vec<Glyph>,
269}
270
271impl Glyphs {
272 pub(crate) fn parse(lines: &mut Lines<'_>, metadata: &Metadata) -> Result<Self, ParserError> {
273 let mut glyphs = Vec::new();
274
275 while let Some(line) = lines.next() {
276 match line.keyword {
277 "CHARS" => {
278 }
280 "STARTCHAR" => {
281 lines.backtrack(line);
282 glyphs.push(Glyph::parse(lines, metadata)?);
283 }
284 "ENDFONT" => {
285 break;
286 }
287 _ => {
288 return Err(ParserError::with_line(
289 &format!("unknown keyword: \"{}\"", line.keyword),
290 &line,
291 ))
292 }
293 }
294 }
295
296 if glyphs.is_empty() {
297 return Err(ParserError::new("no CHARS in font"));
298 }
299
300 Ok(Self { glyphs })
301 }
302
303 pub fn get(&self, c: char) -> Option<&Glyph> {
305 let encoding = Encoding::Standard(c as u32);
307
308 self.glyphs
309 .binary_search_by_key(&encoding, |glyph| glyph.encoding)
310 .map_or(None, |i| Some(&self.glyphs[i]))
311 }
312
313 pub fn contains(&self, c: char) -> bool {
315 self.get(c).is_some()
316 }
317
318 pub fn iter(&self) -> impl Iterator<Item = &Glyph> {
320 self.glyphs.iter()
321 }
322
323 pub(crate) fn approximate_ascent(&self) -> u32 {
327 self.glyphs
328 .iter()
329 .map(|glyph| glyph.bounding_box.size.y - glyph.bounding_box.offset.y)
330 .max()
331 .unwrap_or_default()
332 .try_into()
333 .unwrap()
334 }
335
336 pub(crate) fn approximate_descent(&self) -> u32 {
340 self.glyphs
341 .iter()
342 .map(|glyph| -glyph.bounding_box.offset.y)
343 .max()
344 .unwrap_or_default()
345 .try_into()
346 .unwrap()
347 }
348}
349
350#[cfg(test)]
351mod tests {
352 use crate::Properties;
353
354 use super::*;
355 use indoc::indoc;
356
357 fn mock_metadata() -> Metadata {
358 Metadata {
359 name: "test".to_string(),
360 point_size: 16,
361 resolution: Coord::new(100, 100),
362 bounding_box: BoundingBox::default(),
363 metrics_set: crate::MetricsSet::Horizontal,
364 properties: Properties::default(),
365 }
366 }
367
368 #[track_caller]
369 fn parse_glyph(input: &str) -> Glyph {
370 let mut lines = Lines::new(input);
371 Glyph::parse(&mut lines, &mock_metadata()).unwrap()
372 }
373
374 #[test]
375 fn test_parse_bitmap() {
376 let prefix = "STARTCHAR 0\nSWIDTH 0 0\nDWIDTH 0 0\nBITMAP\n";
377 let suffix = "\nENDCHAR";
378
379 for (input, expected) in [
380 ("7e", vec![0x7e]),
381 ("ff", vec![0xff]),
382 ("CCCC", vec![0xcc, 0xcc]),
383 ("ffffffff", vec![0xff, 0xff, 0xff, 0xff]),
384 (
385 "ffffffff\naaaaaaaa",
386 vec![0xff, 0xff, 0xff, 0xff, 0xaa, 0xaa, 0xaa, 0xaa],
387 ),
388 (
389 "ff\nff\nff\nff\naa\naa\naa\naa",
390 vec![0xff, 0xff, 0xff, 0xff, 0xaa, 0xaa, 0xaa, 0xaa],
391 ),
392 (
393 "00\n00\n00\n00\n18\n24\n24\n42\n42\n7E\n42\n42\n42\n42\n00\n00",
394 vec![
395 0x00, 0x00, 0x00, 0x00, 0x18, 0x24, 0x24, 0x42, 0x42, 0x7e, 0x42, 0x42, 0x42,
396 0x42, 0x00, 0x00,
397 ],
398 ),
399 ] {
400 let glyph = parse_glyph(&format!("{prefix}{input}{suffix}"));
401 assert_eq!(glyph.bitmap, expected);
402 }
403 }
404
405 fn test_data() -> (&'static str, Glyph) {
407 (
408 indoc! {r#"
409 STARTCHAR ZZZZ
410 ENCODING 65
411 SWIDTH 500 0
412 DWIDTH 8 0
413 BBX 8 16 0 -2
414 BITMAP
415 00
416 00
417 00
418 00
419 18
420 24
421 24
422 42
423 42
424 7E
425 42
426 42
427 42
428 42
429 00
430 00
431 ENDCHAR
432 "#},
433 Glyph {
434 name: "ZZZZ".to_string(),
435 encoding: Encoding::Standard(65), bitmap: vec![
437 0x00, 0x00, 0x00, 0x00, 0x18, 0x24, 0x24, 0x42, 0x42, 0x7e, 0x42, 0x42, 0x42,
438 0x42, 0x00, 0x00,
439 ],
440 bounding_box: BoundingBox {
441 size: Coord::new(8, 16),
442 offset: Coord::new(0, -2),
443 },
444 width_horizontal: Some(GlyphWidth {
445 scalable: Coord::new(500, 0),
446 device: Coord::new(8, 0),
447 }),
448 width_vertical: None,
449 origin_offset: None,
450 },
451 )
452 }
453
454 #[test]
455 fn parse_single_char() {
456 let (chardata, expected_glyph) = test_data();
457 assert_eq!(parse_glyph(chardata), expected_glyph);
458 }
459
460 #[test]
461 fn get_glyph_by_char() {
462 let (chardata, expected_glyph) = test_data();
463
464 let mut lines = Lines::new(chardata);
465
466 let glyphs = Glyphs::parse(&mut lines, &mock_metadata()).unwrap();
467 assert_eq!(glyphs.get('A'), Some(&expected_glyph));
468 }
469
470 #[test]
471 fn pixel_getter() {
472 let (chardata, _) = test_data();
473 let glyph = parse_glyph(chardata);
474
475 let bitmap = (0..16)
476 .map(|y| {
477 (0..8)
478 .map(|x| if glyph.pixel(x, y).unwrap() { '#' } else { ' ' })
479 .collect::<String>()
480 })
481 .collect::<Vec<_>>();
482
483 assert_eq!(
484 bitmap,
485 [
486 " ", " ", " ", " ", " ## ", " # # ", " # # ", " # # ", " # # ", " ###### ", " # # ", " # # ", " # # ", " # # ", " ", " ", ]
503 .iter()
504 .map(|s| s.to_string())
505 .collect::<Vec<_>>()
506 );
507 }
508
509 #[test]
510 fn pixels_iterator() {
511 let (chardata, _) = test_data();
512 let glyph = parse_glyph(chardata);
513
514 let bitmap = glyph
515 .pixels()
516 .map(|v| if v { '#' } else { ' ' })
517 .collect::<String>();
518
519 assert_eq!(
520 bitmap,
521 concat!(
522 " ", " ", " ", " ", " ## ", " # # ", " # # ", " # # ", " # # ", " ###### ", " # # ", " # # ", " # # ", " # # ", " ", " ", )
539 );
540 }
541
542 #[test]
543 fn pixel_getter_outside() {
544 let (chardata, _) = test_data();
545 let glyph = parse_glyph(chardata);
546
547 assert_eq!(glyph.pixel(8, 0), None);
548 assert_eq!(glyph.pixel(0, 16), None);
549 assert_eq!(glyph.pixel(8, 16), None);
550 }
551
552 #[test]
553 fn parse_glyph_with_no_encoding() {
554 let chardata = indoc! {r#"
555 STARTCHAR 000
556 ENCODING -1
557 SWIDTH 432 0
558 DWIDTH 6 0
559 BBX 0 0 0 0
560 BITMAP
561 ENDCHAR
562 "#};
563
564 assert_eq!(
565 parse_glyph(chardata),
566 Glyph {
567 bitmap: vec![],
568 bounding_box: BoundingBox {
569 size: Coord::new(0, 0),
570 offset: Coord::new(0, 0),
571 },
572 encoding: Encoding::Unspecified,
573 name: "000".to_string(),
574 width_horizontal: Some(GlyphWidth {
575 scalable: Coord::new(432, 0),
576 device: Coord::new(6, 0),
577 }),
578 width_vertical: None,
579 origin_offset: None,
580 }
581 );
582 }
583
584 #[test]
585 fn parse_glyph_with_no_encoding_and_index() {
586 let chardata = indoc! {r#"
587 STARTCHAR 000
588 ENCODING -1 123
589 SWIDTH 432 0
590 DWIDTH 6 0
591 BBX 0 0 0 0
592 BITMAP
593 ENDCHAR
594 "#};
595
596 assert_eq!(
597 parse_glyph(chardata),
598 Glyph {
599 bitmap: vec![],
600 bounding_box: BoundingBox {
601 size: Coord::new(0, 0),
602 offset: Coord::new(0, 0),
603 },
604 encoding: Encoding::NonStandard(123),
605 name: "000".to_string(),
606 width_horizontal: Some(GlyphWidth {
607 scalable: Coord::new(432, 0),
608 device: Coord::new(6, 0),
609 }),
610 width_vertical: None,
611 origin_offset: None,
612 }
613 );
614 }
615
616 #[test]
617 fn parse_glyph_with_writing_mode1_metrics() {
618 let chardata = indoc! {r#"
619 STARTCHAR 000
620 ENCODING -1
621 SWIDTH1 0 432
622 DWIDTH1 0 6
623 VVECTOR 1 2
624 BBX 0 0 0 0
625 BITMAP
626 ENDCHAR
627 "#};
628
629 assert_eq!(
630 parse_glyph(chardata),
631 Glyph {
632 bitmap: vec![],
633 bounding_box: BoundingBox {
634 size: Coord::new(0, 0),
635 offset: Coord::new(0, 0),
636 },
637 encoding: Encoding::Unspecified,
638 name: "000".to_string(),
639 width_horizontal: None,
640 width_vertical: Some(GlyphWidth {
641 scalable: Coord::new(0, 432),
642 device: Coord::new(0, 6),
643 }),
644 origin_offset: Some(Coord::new(1, 2)),
645 }
646 );
647 }
648
649 #[test]
650 fn parse_glyph_with_empty_bitmap() {
651 let chardata = indoc! {r#"
652 STARTCHAR 000
653 ENCODING 0
654 SWIDTH 432 0
655 DWIDTH 6 0
656 BBX 0 0 0 0
657 BITMAP
658 ENDCHAR
659 "#};
660
661 assert_eq!(
662 parse_glyph(chardata),
663 Glyph {
664 bitmap: vec![],
665 bounding_box: BoundingBox {
666 size: Coord::new(0, 0),
667 offset: Coord::new(0, 0),
668 },
669 encoding: Encoding::Standard(0),
670 name: "000".to_string(),
671 width_horizontal: Some(GlyphWidth {
672 scalable: Coord::new(432, 0),
673 device: Coord::new(6, 0),
674 }),
675 width_vertical: None,
676 origin_offset: None,
677 }
678 );
679 }
680}