1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
//! A super simple no_std psf2 parser for rust.
//! 
//! The psfu format is what's used in the linux tty.
//! You can find the built in psf2 fonts in /usr/share/kbd/consolefonts.
//!
//! This doesn't support the original psf yet, and currently doesn't support glyphs that aren't 8px wide.

#![no_std]

/// The typo is intentional now :^)
const FILE_ZIZE: usize = 4969;

/// The font header.
#[derive(Clone, Copy, Debug)]
pub struct Header {
    pub magic: [u8; 4],
    pub version: u32,
    pub size: u32,
    pub flags: u32,
    pub length: u32,
    pub glyph_size: u32,
    pub glyph_height: u32,
    pub glyph_width: u32,
}

/// The structure for the font.
///
/// # Example
///
/// ```rust
/// let font = Font::load(fs::read("./font.psfu").unwrap().as_slice());
///
/// font.get_char('A', |bit, x, y| {
///    // Stuff
/// });
/// ```
#[derive(Debug)]
pub struct Font {
    pub header: Header,
    char_data: [u8; FILE_ZIZE - 32],
}

impl Font {
    /// # Arguments
    ///
    /// * `char` - Pretty self explanitory. A character, that must be ASCII.
    /// * `action` - A closure that takes in 3 values, the bit (always 0 or 1), the x, and the y.
    pub fn get_char(&self, char: char, mut action: impl FnMut(u8, u8, u8)) {
        let char = char as u32;

        let from = self.header.glyph_size * (char);
        let to = self.header.glyph_size * (char + 1);

        for (i, byte) in self.char_data[from as usize..to as usize]
            .iter()
            .enumerate()
        {
            if byte == &0 {
                continue;
            }

            for j in 0..8 {
                // Bit is a u8 that is always either a 0 or a 1.
                // "But why not use a boolean?" I hear you ask.
                // Every variable in rust is always at least one byte in size,
                // So it doesn't do much for saving memory.
                let bit = (byte >> (7 - j)) & 1;

                action(bit, j, i as u8);
            }
        }
    }

    pub fn load(raw: &[u8]) -> Font {
        let size = as_u32_le(&raw[0x8..0xc]);

        let font = Font {
            header: Header {
                magic: [raw[0x0], raw[0x1], raw[0x2], raw[0x3]],
                version: as_u32_le(&raw[0x4..0x8]),
                size,
                flags: as_u32_le(&raw[0xc..0x10]),
                length: as_u32_le(&raw[0x10..0x14]),
                glyph_size: as_u32_le(&raw[0x14..0x18]),
                glyph_height: as_u32_le(&raw[0x18..0x1c]),
                glyph_width: as_u32_le(&raw[0x1c..0x20]),
            },
            char_data: raw[size as usize..].try_into().unwrap(),
        };

        if font.header.magic != [0x72, 0xb5, 0x4a, 0x86] {
            panic!("header magic does not match, is this a psf2 font?");
        }

        font
    }
}

fn as_u32_le(array: &[u8]) -> u32 {
    (array[0] as u32)
        + ((array[1] as u32) << 8)
        + ((array[2] as u32) << 16)
        + ((array[3] as u32) << 24)
}