bmfont/
lib.rs

1//! Parser for bitmap fonts
2
3#[cfg(feature = "serde")]
4#[macro_use]
5extern crate serde;
6
7mod char;
8mod config_parse_error;
9mod error;
10mod kerning_value;
11mod page;
12mod rect;
13mod sections;
14mod string_parse_error;
15mod utils;
16
17pub use self::config_parse_error::ConfigParseError;
18pub use self::error::Error;
19pub use self::rect::Rect;
20pub use self::string_parse_error::StringParseError;
21
22use self::char::Char;
23use self::kerning_value::KerningValue;
24use self::page::Page;
25use self::sections::Sections;
26use std::io::Read;
27use std::iter::Peekable;
28use std::str::Chars;
29
30/// Alias of either [`Result<Vec<CharPosition>, StringParseError>`] _or_ [`Vec<CharPosition>`],
31/// returned by [`BMFont::parse()`].
32///
33/// The output type depends on the value of the `parse-error` package feature.
34///
35/// **_NOTE:_** This documentation was generated _with_ the `parse-error` feature.
36#[cfg(feature = "parse-error")]
37pub type Parse<'a> = Result<ParseIter<'a>, StringParseError>;
38
39/// Alias of either [`Result<Vec<CharPosition>, StringParseError>`] _or_ [`Vec<CharPosition>`],
40/// returned by [`BMFont::parse()`].
41///
42/// The output type depends on the value of the `parse-error` package feature.
43///
44/// **_NOTE:_** This documentation was generated _without_ the `parse-error` feature.
45#[cfg(not(feature = "parse-error"))]
46pub type Parse<'a> = ParseIter<'a>;
47
48#[cfg(feature = "parse-error")]
49type ParseLines<'a> = Result<LineIter<'a>, StringParseError>;
50
51#[cfg(not(feature = "parse-error"))]
52type ParseLines<'a> = LineIter<'a>;
53
54#[derive(Clone, Debug)]
55pub struct CharPosition {
56    pub page_rect: Rect,
57    pub screen_rect: Rect,
58    pub page_index: u32,
59}
60
61#[derive(Clone, Debug)]
62#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
63#[cfg_attr(feature = "serde_json", derive(Eq, PartialEq))]
64pub enum OrdinateOrientation {
65    BottomToTop,
66    TopToBottom,
67}
68
69/// Holds a decoded bitmap font defintion, including all character advance and kerning values.
70#[derive(Clone, Debug)]
71#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
72#[cfg_attr(feature = "serde_json", derive(Eq, PartialEq))]
73pub struct BMFont {
74    base_height: u32,
75    line_height: u32,
76    characters: Vec<Char>,
77    kerning_values: Vec<KerningValue>,
78    pages: Vec<Page>,
79    ordinate_orientation: OrdinateOrientation,
80}
81
82impl BMFont {
83    /// Constructs a new [BMFont].
84    ///
85    /// # Examples
86    ///
87    /// From a file:
88    ///
89    /// ```rust
90    /// # use bmfont::*;
91    /// # fn main() -> Result<(), Error> {
92    /// let file = std::fs::File::open("font.fnt")?;
93    /// let font = BMFont::new(file, OrdinateOrientation::TopToBottom)?;
94    /// assert_eq!(font.line_height(), 80);
95    /// #     Ok(())
96    /// # }
97    /// ```
98    ///
99    /// From a slice of bytes:
100    ///
101    /// ```rust
102    /// # use bmfont::*;
103    /// # fn main() -> Result<(), Error> {
104    /// # let my_font_bytes = std::fs::read("font.fnt")?;
105    /// let data = std::io::Cursor::new(my_font_bytes);
106    /// let font = BMFont::new(data, OrdinateOrientation::TopToBottom)?;
107    /// assert_eq!(font.line_height(), 80);
108    /// #     Ok(())
109    /// # }
110    /// ```
111    pub fn new<R>(source: R, ordinate_orientation: OrdinateOrientation) -> Result<BMFont, Error>
112    where
113        R: Read,
114    {
115        let sections = Sections::new(source)?;
116
117        let base_height;
118        let line_height;
119        {
120            let mut components = sections.common_section.split_whitespace();
121            components.next();
122            line_height =
123                utils::extract_component_value(components.next(), "common", "lineHeight")?;
124            base_height = utils::extract_component_value(components.next(), "common", "base")?;
125        }
126
127        let mut pages = Vec::with_capacity(sections.page_sections.len());
128        for page_section in &sections.page_sections {
129            pages.push(Page::new(page_section)?);
130        }
131
132        // Sort the characters while loading them so that lookup can be faster during parse
133        let mut characters: Vec<Char> = Vec::with_capacity(sections.char_sections.len());
134        for char_section in &sections.char_sections {
135            let char = Char::new(char_section)?;
136            if let Err(idx) = characters.binary_search_by(|probe| probe.id.cmp(&char.id)) {
137                characters.insert(idx, char);
138            }
139        }
140
141        // Also sort kerning values for the same reason, but we allow duplicates
142        let mut kerning_values: Vec<KerningValue> =
143            Vec::with_capacity(sections.kerning_sections.len());
144        for kerning_section in &sections.kerning_sections {
145            let kerning = KerningValue::new(kerning_section)?;
146
147            match kerning_values
148                .binary_search_by(|probe| probe.first_char_id.cmp(&kerning.first_char_id))
149            {
150                Err(idx) | Ok(idx) => kerning_values.insert(idx, kerning),
151            }
152        }
153
154        Ok(BMFont {
155            base_height,
156            line_height,
157            characters,
158            kerning_values,
159            pages,
160            ordinate_orientation,
161        })
162    }
163
164    /// Returns the height of a `EM` in pixels.
165    pub fn base_height(&self) -> u32 {
166        self.base_height
167    }
168
169    pub fn line_height(&self) -> u32 {
170        self.line_height
171    }
172
173    /// Returns an `Iterator` of font page bitmap filenames.
174    ///
175    /// # Examples
176    ///
177    /// ```rust
178    /// # use bmfont::*;
179    /// # fn main() -> Result<(), Error> {
180    /// let file = std::fs::File::open("font.fnt")?;
181    /// let font = BMFont::new(file, OrdinateOrientation::TopToBottom)?;
182    /// assert_eq!(font.pages().next(), Some("font.png"));
183    /// #     Ok(())
184    /// # }
185    /// ```
186    pub fn pages(&self) -> PageIter {
187        PageIter::new(&self.pages)
188    }
189
190    pub fn parse<'s>(&'s self, s: &'s str) -> Parse<'s> {
191        let lines = self.parse_lines(s);
192
193        #[cfg(feature = "parse-error")]
194        let lines = lines?;
195
196        let char_positions = ParseIter::new(self, lines);
197
198        #[cfg(feature = "parse-error")]
199        {
200            Ok(char_positions)
201        }
202
203        #[cfg(not(feature = "parse-error"))]
204        {
205            char_positions
206        }
207    }
208
209    fn find_kerning_values(&self, first_char_id: u32) -> KerningIter {
210        let needle = (first_char_id << 1) - 1;
211        let idx = self
212            .kerning_values
213            .binary_search_by(|probe| (probe.first_char_id << 1).cmp(&needle))
214            .unwrap_err();
215
216        KerningIter {
217            first_char_id,
218            idx,
219            values: &self.kerning_values,
220        }
221    }
222
223    fn parse_lines<'a>(&'a self, s: &'a str) -> ParseLines<'a> {
224        #[cfg(feature = "parse-error")]
225        {
226            let mut temp = [0u16; 2];
227            let mut missing_characters: Option<Vec<char>> = None;
228            let mut unsupported_characters: Option<Vec<char>> = None;
229
230            for c in s.chars() {
231                if c == '\n' {
232                    continue;
233                } else if c.len_utf16() != 1 {
234                    if let Some(vec) = unsupported_characters.as_mut() {
235                        vec.push(c);
236                    } else {
237                        unsupported_characters = Some(vec![c]);
238                    }
239
240                    continue;
241                }
242
243                c.encode_utf16(&mut temp);
244                let char_id = temp[0] as u32;
245
246                if self
247                    .characters
248                    .binary_search_by(|probe| probe.id.cmp(&char_id))
249                    .is_ok()
250                {
251                    continue;
252                }
253
254                if let Some(vec) = missing_characters.as_mut() {
255                    vec.push(c);
256                } else {
257                    missing_characters = Some(vec![c]);
258                }
259            }
260
261            if missing_characters.is_some() || unsupported_characters.is_some() {
262                return Err(StringParseError {
263                    missing_characters: missing_characters.unwrap_or_default(),
264                    unsupported_characters: unsupported_characters.unwrap_or_default(),
265                });
266            }
267        }
268
269        let lines = LineIter {
270            characters: &self.characters,
271            text: Some(s.chars().peekable()),
272        };
273
274        #[cfg(feature = "parse-error")]
275        {
276            Ok(lines)
277        }
278
279        #[cfg(not(feature = "parse-error"))]
280        lines
281    }
282}
283
284struct CharIter<'a> {
285    characters: &'a Vec<Char>,
286    text: Peekable<Chars<'a>>,
287}
288
289impl<'a> Iterator for CharIter<'a> {
290    type Item = &'a Char;
291
292    fn next(&mut self) -> Option<Self::Item> {
293        loop {
294            return match self.text.next() {
295                None | Some('\n') => None,
296                Some(chr) if chr.len_utf16() != 1 => continue,
297                Some(chr) => {
298                    let mut temp = [0u16; 2];
299                    chr.encode_utf16(&mut temp);
300                    let char_id = temp[0] as u32;
301                    let char_idx = self
302                        .characters
303                        .binary_search_by(|probe| probe.id.cmp(&char_id));
304
305                    #[cfg(not(feature = "parse-error"))]
306                    if char_idx.is_err() {
307                        continue;
308                    }
309
310                    let char_idx = char_idx.unwrap();
311                    Some(&self.characters[char_idx])
312                }
313            };
314        }
315    }
316}
317
318struct KerningIter<'a> {
319    first_char_id: u32,
320    idx: usize,
321    values: &'a Vec<KerningValue>,
322}
323
324impl<'a> KerningIter<'a> {
325    fn empty(values: &'a Vec<KerningValue>) -> Self {
326        Self {
327            first_char_id: 0,
328            idx: values.len(),
329            values,
330        }
331    }
332}
333
334impl<'a> Iterator for KerningIter<'a> {
335    type Item = &'a KerningValue;
336
337    fn next(&mut self) -> Option<Self::Item> {
338        if let Some(value) = self.values.get(self.idx) {
339            if value.first_char_id == self.first_char_id {
340                self.idx += 1;
341
342                return Some(value);
343            }
344        }
345        None
346    }
347}
348
349struct LineIter<'a> {
350    characters: &'a Vec<Char>,
351    text: Option<Peekable<Chars<'a>>>,
352}
353
354impl<'a> Iterator for LineIter<'a> {
355    type Item = CharIter<'a>;
356
357    fn next(&mut self) -> Option<Self::Item> {
358        match self.text.as_mut().unwrap().peek() {
359            Some(_) => Some(CharIter {
360                characters: &self.characters,
361                text: self.text.take().unwrap(),
362            }),
363            _ => None,
364        }
365    }
366}
367
368#[derive(Clone, Debug)]
369pub struct PageIter<'a> {
370    idx: usize,
371    pages: &'a Vec<Page>,
372}
373
374impl<'a> PageIter<'a> {
375    fn new(pages: &'a Vec<Page>) -> Self {
376        Self { idx: 0, pages }
377    }
378}
379
380impl<'a> Iterator for PageIter<'a> {
381    type Item = &'a str;
382
383    fn next(&mut self) -> Option<Self::Item> {
384        if let Some(page) = self.pages.get(self.idx) {
385            self.idx += 1;
386            Some(page.file.as_str())
387        } else {
388            None
389        }
390    }
391}
392
393pub struct ParseIter<'a> {
394    font: &'a BMFont,
395    line: Option<ParseLineIter<'a>>,
396    lines: LineIter<'a>,
397    y: i32,
398}
399
400impl<'a> ParseIter<'a> {
401    fn new(font: &'a BMFont, lines: LineIter<'a>) -> Self {
402        Self {
403            font,
404            line: None,
405            lines,
406            y: 0,
407        }
408    }
409}
410
411impl<'a> Iterator for ParseIter<'a> {
412    type Item = CharPosition;
413
414    fn next(&mut self) -> Option<Self::Item> {
415        loop {
416            if self.line.is_none() {
417                if let Some(chars) = self.lines.next() {
418                    self.line = Some(ParseLineIter::new(&self.font, chars, self.y));
419                } else {
420                    return None;
421                }
422            }
423
424            let line = self.line.as_mut().unwrap();
425            if let Some(char_position) = line.next() {
426                return Some(char_position);
427            }
428
429            self.lines
430                .text
431                .replace(self.line.take().unwrap().chars.text);
432            match self.font.ordinate_orientation {
433                OrdinateOrientation::TopToBottom => self.y += self.font.line_height as i32,
434                OrdinateOrientation::BottomToTop => self.y -= self.font.line_height as i32,
435            }
436        }
437    }
438}
439
440struct ParseLineIter<'a> {
441    font: &'a BMFont,
442    chars: CharIter<'a>,
443    kerning_values: KerningIter<'a>,
444    x: i32,
445    y: i32,
446}
447
448impl<'a> ParseLineIter<'a> {
449    fn new(font: &'a BMFont, chars: CharIter<'a>, y: i32) -> Self {
450        Self {
451            font,
452            chars,
453            kerning_values: KerningIter::empty(&font.kerning_values),
454            x: 0,
455            y,
456        }
457    }
458}
459
460impl<'a> Iterator for ParseLineIter<'a> {
461    type Item = CharPosition;
462
463    fn next(&mut self) -> Option<Self::Item> {
464        match self.chars.next() {
465            Some(char) => {
466                let kerning_value = self
467                    .kerning_values
468                    .find(|k| k.second_char_id == char.id)
469                    .map(|k| k.value)
470                    .unwrap_or(0);
471                let page_rect = Rect {
472                    x: char.x as i32,
473                    y: char.y as i32,
474                    width: char.width,
475                    height: char.height,
476                };
477                let screen_x = self.x + char.xoffset + kerning_value;
478                let screen_y = match self.font.ordinate_orientation {
479                    OrdinateOrientation::BottomToTop => {
480                        self.y + self.font.base_height as i32 - char.yoffset - char.height as i32
481                    }
482                    OrdinateOrientation::TopToBottom => self.y + char.yoffset,
483                };
484                let screen_rect = Rect {
485                    x: screen_x,
486                    y: screen_y,
487                    width: char.width,
488                    height: char.height,
489                };
490                let char_position = CharPosition {
491                    page_rect,
492                    screen_rect,
493                    page_index: char.page_index,
494                };
495                self.x += char.xadvance + kerning_value;
496                self.kerning_values = self.font.find_kerning_values(char.id);
497
498                Some(char_position)
499            }
500            _ => None,
501        }
502    }
503}