bmf_parser/
binary.rs

1/*---------------------------------------------------------------------------------------------
2 *  Copyright (c) Peter Bjorklund. All rights reserved.
3 *  Licensed under the MIT License. See LICENSE in the project root for license information.
4 *--------------------------------------------------------------------------------------------*/
5
6use crate::{BMFont, Char, CommonBlock, InfoBlock, KerningPair};
7use byteorder::{LittleEndian, ReadBytesExt};
8use std::collections::HashMap;
9use std::io::BufRead;
10use std::io::{self, Cursor, Read};
11
12pub fn from_octets(data: &[u8]) -> io::Result<BMFont> {
13    let mut cursor = Cursor::new(data);
14
15    if cursor.read_u8()? != 66
16        || cursor.read_u8()? != 77
17        || cursor.read_u8()? != 70
18        || cursor.read_u8()? != 3
19    {
20        return Err(io::Error::new(
21            io::ErrorKind::InvalidData,
22            "Invalid BMFont header",
23        ));
24    }
25
26    let mut info = None;
27    let mut common = None;
28    let mut pages = Vec::new();
29    let mut chars = HashMap::new();
30    let mut kernings = Vec::new();
31
32    while let Ok(block_type) = cursor.read_u8() {
33        let block_size = cursor.read_u32::<LittleEndian>()? as usize;
34        let mut block_data = vec![0; block_size];
35        cursor.read_exact(&mut block_data)?;
36
37        match block_type {
38            1 => info = Some(parse_info_block(&block_data)?),
39            2 => common = Some(parse_common_block(&block_data)?),
40            3 => pages = parse_pages_block(&block_data)?,
41            4 => chars = parse_chars_block(&block_data)?,
42            5 => kernings = parse_kerning_block(&block_data)?,
43            _ => (),
44        }
45    }
46
47    Ok(BMFont {
48        info,
49        common,
50        pages,
51        chars,
52        kernings,
53    })
54}
55
56fn parse_info_block(data: &[u8]) -> io::Result<InfoBlock> {
57    let mut cursor = Cursor::new(data);
58    Ok(InfoBlock {
59        font_size: cursor.read_i16::<LittleEndian>()?,
60        bit_field: cursor.read_u8()?,
61        char_set: cursor.read_u8()?,
62        stretch_h: cursor.read_u16::<LittleEndian>()?,
63        aa: cursor.read_u8()?,
64        padding: [
65            cursor.read_u8()?,
66            cursor.read_u8()?,
67            cursor.read_u8()?,
68            cursor.read_u8()?,
69        ],
70        spacing: [cursor.read_u8()?, cursor.read_u8()?],
71        outline: cursor.read_u8()?,
72        font_name: {
73            let mut font_name = Vec::new();
74            cursor.read_to_end(&mut font_name)?;
75            String::from_utf8(font_name)
76                .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?
77                .trim_end_matches('\0')
78                .to_string()
79        },
80    })
81}
82
83fn parse_common_block(data: &[u8]) -> io::Result<CommonBlock> {
84    let mut cursor = Cursor::new(data);
85    Ok(CommonBlock {
86        line_height: cursor.read_u16::<LittleEndian>()?,
87        base: cursor.read_u16::<LittleEndian>()?,
88        scale_w: cursor.read_u16::<LittleEndian>()?,
89        scale_h: cursor.read_u16::<LittleEndian>()?,
90        pages: cursor.read_u16::<LittleEndian>()?,
91        bit_field: cursor.read_u8()?,
92        alpha_chnl: cursor.read_u8()?,
93        red_chnl: cursor.read_u8()?,
94        green_chnl: cursor.read_u8()?,
95        blue_chnl: cursor.read_u8()?,
96    })
97}
98
99fn parse_pages_block(data: &[u8]) -> io::Result<Vec<String>> {
100    let mut cursor = Cursor::new(data);
101    let mut pages = Vec::new();
102    while cursor.position() < data.len() as u64 {
103        let mut page_name = Vec::new();
104        cursor.read_until(0, &mut page_name)?;
105        pages.push(
106            String::from_utf8(page_name)
107                .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?
108                .trim_end_matches('\0')
109                .to_string(),
110        );
111    }
112    Ok(pages)
113}
114
115fn parse_chars_block(data: &[u8]) -> io::Result<HashMap<u32, Char>> {
116    let mut cursor = Cursor::new(data);
117    let mut chars = HashMap::new();
118    while cursor.position() < data.len() as u64 {
119        let ch = Char {
120            id: cursor.read_u32::<LittleEndian>()?,
121            x: cursor.read_u16::<LittleEndian>()?,
122            y: cursor.read_u16::<LittleEndian>()?,
123            width: cursor.read_u16::<LittleEndian>()?,
124            height: cursor.read_u16::<LittleEndian>()?,
125            x_offset: cursor.read_i16::<LittleEndian>()?,
126            y_offset: cursor.read_i16::<LittleEndian>()?,
127            x_advance: cursor.read_i16::<LittleEndian>()?,
128            page: cursor.read_u8()?,
129            chnl: cursor.read_u8()?,
130        };
131        chars.insert(ch.id, ch);
132    }
133    Ok(chars)
134}
135
136fn parse_kerning_block(data: &[u8]) -> io::Result<Vec<KerningPair>> {
137    let mut cursor = Cursor::new(data);
138    let mut kernings = Vec::new();
139    while cursor.position() < data.len() as u64 {
140        kernings.push(KerningPair {
141            first: cursor.read_u32::<LittleEndian>()?,
142            second: cursor.read_u32::<LittleEndian>()?,
143            amount: cursor.read_i16::<LittleEndian>()?,
144        });
145    }
146    Ok(kernings)
147}