#![no_std]
#![doc = include_str!("../README.md")]
pub struct Psf<'a> {
pub glyph_byte_length: usize,
pub glyph_width: usize,
pub glyph_height: usize,
pub glyphs: &'a [u8],
pub unicode_table: Option<&'a [u8]>,
}
impl<'a> Psf<'a> {
pub const fn parse(data: &[u8]) -> Result<Psf, ParseError> {
if data.len() < 32 {
return Err(ParseError::HeaderMissing);
}
if u32_at(data, 0) == 0x864ab572 {
Self::parse_psf2(data)
} else if u32_at(data, 0) & 0xffff == 0x0436 {
Self::parse_psf1(data)
} else {
Err(ParseError::InvalidMagicBytes)
}
}
const fn parse_psf1(data: &[u8]) -> Result<Psf, ParseError> {
let mode = data[2];
let has_512_glyphs = 0 != (mode & 0x1);
let has_unicode_table = 0 != (mode & 0x2);
let glyph_count =
if has_512_glyphs {
256
} else {
512
};
let glyph_height = data[3] as usize;
let glyphs = slice_len(data, 4, glyph_count * glyph_height);
let unicode_entries =
if has_unicode_table {
let (unicode_entries, _) = data.split_at(4 + glyph_count * glyph_height);
Some(unicode_entries)
} else {
None
};
let psf =
Psf {
glyph_byte_length: glyph_height,
glyph_width: 8,
glyph_height: glyph_height,
glyphs: glyphs,
unicode_table: unicode_entries,
};
Ok(psf)
}
const fn parse_psf2(data: &[u8]) -> Result<Psf, ParseError> {
let version = u32_at(data, 4);
if 0 != version {
return Err(ParseError::UnknownVersion(version));
}
let header_size = u32_at(data, 8) as usize;
let flags = u32_at(data, 12);
let has_unicode_table = 1 == (flags & 0x00000001);
let glyph_count = u32_at(data, 16) as usize;
let glyph_byte_length = u32_at(data, 20) as usize;
let glyph_height = u32_at(data, 24) as usize;
let glyph_width = u32_at(data, 28) as usize;
let expected_byte_count = header_size + (glyph_count * glyph_byte_length);
if data.len() < expected_byte_count {
return Err(ParseError::GlyphTableTruncated { expected_byte_count: expected_byte_count });
}
let glyphs = slice_len(data, header_size, glyph_byte_length * glyph_count);
let unicode_table =
if has_unicode_table {
Some(data.split_at(expected_byte_count).1)
} else {
None
};
let psf = Psf {
glyph_byte_length: glyph_byte_length,
glyph_width: glyph_width,
glyph_height: glyph_height,
glyphs,
unicode_table,
};
Ok(psf)
}
pub fn get_glyph_bits(&self, glyph_index: usize) -> Option<&[u8]> {
let start = self.glyph_byte_length * glyph_index;
let end = start + self.glyph_byte_length;
self.glyphs.get(start..end)
}
pub fn get_glyph_pixels<'b>(&'b self, glyph_index: usize) -> Option<impl Iterator<Item=bool> + 'b> {
let glyph_bits = self.get_glyph_bits(glyph_index)?;
let bytes_per_row = (self.glyph_width + 7) / 8;
let iterator =
(0..self.glyph_height).flat_map(move |y| {
(0..self.glyph_width).map(move |x| {
let byte_index = (x / 8) + (y * bytes_per_row);
let byte = glyph_bits[byte_index];
let bit_offset = 7 - (x % 8);
let bit = (byte >> bit_offset) & 1;
bit == 1
})
});
Some(iterator)
}
pub fn iter_unicode_entries<'b>(&'b self) -> Option<impl Iterator<Item=(usize, Result<&'b str, core::str::Utf8Error>)> + 'b> {
let table = self.unicode_table?;
let iterator = table.split(|&x| x == 0xff).enumerate().flat_map(move |(glyph_index, unicode_entries)| {
unicode_entries.split(|&x| x == 0xfe).map(move |unicode_string| {
(glyph_index, core::str::from_utf8(unicode_string))
})
});
Some(iterator)
}
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum ParseError {
HeaderMissing,
InvalidMagicBytes,
UnknownVersion(u32),
GlyphTableTruncated { expected_byte_count: usize, },
}
const fn slice_len(data: &[u8], start: usize, len: usize) -> &[u8] {
let (_, rest) = data.split_at(start);
let (segment, _) = rest.split_at(len);
segment
}
const fn u32_at(data: &[u8], at: usize) -> u32 {
let bytes = slice_len(data, at, 4);
u32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]])
}