bdf_parser/
metadata.rs

1use nom::{
2    character::complete::{multispace0, space1},
3    combinator::map_opt,
4    sequence::separated_pair,
5    IResult, ParseTo,
6};
7
8use crate::{helpers::*, BoundingBox, Coord};
9
10/// BDF file metadata.
11#[derive(Debug, Clone, PartialEq)]
12pub struct Metadata {
13    /// BDF format version.
14    pub version: f32,
15
16    /// Font name.
17    pub name: String,
18
19    /// Point size.
20    pub point_size: i32,
21
22    /// X and Y resolution in DPI.
23    pub resolution: Coord,
24
25    /// Font bounding box.
26    pub bounding_box: BoundingBox,
27}
28
29impl Metadata {
30    pub(crate) fn parse(input: &[u8]) -> IResult<&[u8], Self> {
31        let (input, version) = skip_comments(metadata_version)(input)?;
32        let (input, name) = skip_comments(metadata_name)(input)?;
33        let (input, (point_size, resolution)) = skip_comments(metadata_size)(input)?;
34        let (input, bounding_box) = skip_comments(metadata_bounding_box)(input)?;
35        let (input, _) = multispace0(input)?;
36
37        Ok((
38            input,
39            Self {
40                version,
41                name,
42                point_size,
43                resolution,
44                bounding_box,
45            },
46        ))
47    }
48}
49
50fn metadata_version(input: &[u8]) -> IResult<&[u8], f32> {
51    map_opt(statement("STARTFONT", parse_string), |v: String| {
52        v.as_str().parse_to()
53    })(input)
54}
55
56fn metadata_name(input: &[u8]) -> IResult<&[u8], String> {
57    statement("FONT", parse_string)(input)
58}
59
60fn metadata_size(input: &[u8]) -> IResult<&[u8], (i32, Coord)> {
61    statement("SIZE", separated_pair(parse_to_i32, space1, Coord::parse))(input)
62}
63
64fn metadata_bounding_box(input: &[u8]) -> IResult<&[u8], BoundingBox> {
65    statement("FONTBOUNDINGBOX", BoundingBox::parse)(input)
66}
67
68#[cfg(test)]
69mod tests {
70    use super::*;
71
72    #[test]
73    fn parse_bdf_version() {
74        assert_parser_ok!(metadata_version(b"STARTFONT 2.1\n"), 2.1f32);
75
76        // Some fonts are a bit overzealous with their whitespace
77        assert_parser_ok!(metadata_version(b"STARTFONT   2.1\n"), 2.1f32);
78    }
79
80    #[test]
81    fn parse_font_name() {
82        assert_parser_ok!(metadata_name(b"FONT abc"), "abc".to_string());
83    }
84
85    #[test]
86    fn parse_metadata() {
87        let input = br#"STARTFONT 2.1
88FONT "test font"
89SIZE 16 75 100
90FONTBOUNDINGBOX 16 24 1 2"#;
91
92        assert_parser_ok!(
93            Metadata::parse(input),
94            Metadata {
95                version: 2.1,
96                name: String::from("\"test font\""),
97                point_size: 16,
98                resolution: Coord::new(75, 100),
99                bounding_box: BoundingBox {
100                    size: Coord::new(16, 24),
101                    offset: Coord::new(1, 2),
102                }
103            }
104        );
105    }
106}