Skip to content

Instantly share code, notes, and snippets.

@hinzundcode
Created July 22, 2021 15:25
Show Gist options
  • Save hinzundcode/0480c5c8aa220cd43cc8da634119a3c0 to your computer and use it in GitHub Desktop.
Save hinzundcode/0480c5c8aa220cd43cc8da634119a3c0 to your computer and use it in GitHub Desktop.
psf font parser in rust
use std::fs;
use std::mem;
// https://www.win.tue.nl/~aeb/linux/kbd/font-formats-1.html
const PSF1_MAGIC: [u8;2] = [0x36, 0x04];
const PSF2_MAGIC: [u8;4] = [0x72, 0xb5, 0x4a, 0x86];
#[derive(Debug)]
enum Error {
OutOfBounds,
InvalidMagic,
}
#[repr(C, packed)]
struct PSF1Header {
magic: [u8;2],
mode: u8,
char_size: u8,
}
#[repr(C, packed)]
struct PSF2Header {
magic: [u8;4],
version: u32,
header_size: u32,
flags: u32,
length: u32,
char_size: u32,
height: u32,
width: u32,
}
enum PSFFont<'a> {
PSF1Font(PSF1Font<'a>),
PSF2Font(PSF2Font<'a>),
}
impl<'a> PSFFont<'a> {
fn parse(data: &'a [u8]) -> Result<Self, Error> {
if data.len() < mem::size_of::<PSF1Header>() {
return Err(Error::OutOfBounds);
}
let header = unsafe {
let ref header = *(data.as_ptr() as *const PSF1Header);
header
};
if header.magic == PSF1_MAGIC {
return Ok(PSFFont::PSF1Font(PSF1Font::parse(data)?));
}
if data.len() < mem::size_of::<PSF2Header>() {
return Err(Error::OutOfBounds);
}
let header = unsafe {
let ref header = *(data.as_ptr() as *const PSF2Header);
header
};
if header.magic == PSF2_MAGIC {
return Ok(PSFFont::PSF2Font(PSF2Font::parse(data)?));
}
return Err(Error::InvalidMagic);
}
fn glyph_size(&self) -> (u32, u32) {
match self {
PSFFont::PSF1Font(font) => font.glyph_size(),
PSFFont::PSF2Font(font) => font.glyph_size(),
}
}
fn glyph_count(&self) -> u32 {
match self {
PSFFont::PSF1Font(font) => font.glyph_count(),
PSFFont::PSF2Font(font) => font.glyph_count(),
}
}
fn glyph(&self, index: u32) -> Option<&[u8]> {
match self {
PSFFont::PSF1Font(font) => font.glyph(index),
PSFFont::PSF2Font(font) => font.glyph(index),
}
}
}
struct PSF1Font<'a> {
data: &'a [u8],
header: &'a PSF1Header,
}
impl<'a> PSF1Font<'a> {
fn parse(data: &'a [u8]) -> Result<Self, Error> {
if data.len() < mem::size_of::<PSF1Header>() {
return Err(Error::OutOfBounds);
}
let header = unsafe {
let ref header = *(data.as_ptr() as *const PSF1Header);
header
};
if header.magic != PSF1_MAGIC {
return Err(Error::InvalidMagic);
}
let last_glyph_pos = mem::size_of::<PSF1Header>() + header.char_size as usize * 256;
if data.len() < last_glyph_pos {
return Err(Error::OutOfBounds);
}
Ok(PSF1Font {
data,
header,
})
}
fn glyph_size(&self) -> (u32, u32) {
(8, self.header.char_size as u32)
}
fn glyph_count(&self) -> u32 {
256
}
fn glyph(&self, index: u32) -> Option<&[u8]> {
if index >= 256 {
return None
}
let length = self.header.char_size as usize;
let offset = mem::size_of::<PSF1Header>() + index as usize * length;
Some(&self.data[offset..(offset+length)])
}
}
struct PSF2Font<'a> {
data: &'a [u8],
header: &'a PSF2Header,
}
impl<'a> PSF2Font<'a> {
fn parse(data: &'a [u8]) -> Result<Self, Error> {
if data.len() < mem::size_of::<PSF2Header>() {
return Err(Error::OutOfBounds);
}
let header = unsafe {
let ref header = *(data.as_ptr() as *const PSF2Header);
header
};
if header.magic != PSF2_MAGIC {
return Err(Error::InvalidMagic);
}
let last_glyph_pos = header.header_size + header.char_size * (header.length - 1);
if data.len() < last_glyph_pos as usize {
return Err(Error::OutOfBounds);
}
Ok(PSF2Font {
data,
header,
})
}
fn glyph_size(&self) -> (u32, u32) {
(self.header.width, self.header.height)
}
fn glyph_count(&self) -> u32 {
self.header.length
}
fn glyph(&self, index: u32) -> Option<&[u8]> {
if index >= self.header.length {
return None
}
let length = self.header.char_size as usize;
let offset = self.header.header_size as usize + index as usize * length;
Some(&self.data[offset..(offset+length)])
}
}
fn print_bitmap(bitmap: &[u8], size: (u32, u32)) {
let stride = (size.0 + 7) / 8;
println!("stride {}", stride);
for y in 0..size.1 {
for x in 0..size.0 {
let byte = y * stride + (x / 8);
let bit = 7 - (x % 8);
if (bitmap[byte as usize] & (1 << bit)) == 0 {
print!("_");
} else {
print!("#");
}
}
println!();
}
}
fn main() -> Result<(), std::io::Error> {
let buffer = fs::read("Lat7-Terminus28x14.psf")?;
//let buffer = fs::read("Lat7-Terminus16.psf")?;
let font = PSFFont::parse(&buffer).unwrap();
println!("glyph count: {}", font.glyph_count());
println!("glyph size: {:?}", font.glyph_size());
for glyph_index in 0..256 {
let glyph = font.glyph(glyph_index).unwrap();
println!("glyph {}: {:?}", glyph_index, glyph);
print_bitmap(glyph, font.glyph_size());
}
Ok(())
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment