/// Build script — extracts glyph data from a PSF2 font file. /// Uses zcat (system gzip) to decompress — zero dependencies. use std::env; use std::fs; use std::path::Path; use std::process::{Command, Output}; fn main() -> Result<(), Box> { let font_path = "/usr/share/kbd/consolefonts/lat1-16.psfu.gz"; println!("cargo::rerun-if-changed={}", font_path); // Decompress via zcat (available on any Linux system) let Output { stdout, status, .. } = Command::new("zcat").arg(font_path).output()?; if !status.success() { return Err(format!("zcat exited with {status:?}").into()); } let raw = stdout; if raw.len() < 32 { return Err("PSF font too small".into()); } // PSF2 magic: 0x864ab572 (little-endian) let magic = u32::from_le_bytes(raw[..4].try_into()?); if magic != 0x864ab572 { return Err("not a PSF2 font".into()); } let hdr_size = u32::from_le_bytes(raw[8..12].try_into()?) as usize; let num_glyphs = u32::from_le_bytes(raw[16..20].try_into()?) as usize; let bytes_per_glyph = u32::from_le_bytes(raw[20..24].try_into()?) as usize; if num_glyphs != 256 { return Err(format!("expected 256 glyphs, got {num_glyphs}").into()); } if bytes_per_glyph != 16 { return Err(format!("expected 16 bytes/glyph, got {bytes_per_glyph}").into()); } let glyph_data = &raw[hdr_size..hdr_size + num_glyphs * bytes_per_glyph]; // Write generated Rust source into OUT_DIR let out_dir = env::var("OUT_DIR")?; let dest = Path::new(&out_dir).join("font_data.rs"); let mut code = String::from( "/// 8×16 bitmap font glyph data (256 glyphs, 16 bytes each). #[rustfmt::skip] #[allow(clippy::unreadable_literal)] pub const GLYPH_8X16: [[u8; 16]; 256] = [", ); for g in 0..256 { let offset = g * 16; code.push_str("\n ["); for row in 0..16 { if row % 4 == 0 { code.push('\n'); } let byte = glyph_data[offset + row]; code.push_str(&format!(" 0x{byte:02X},")); } code.push_str("\n ],"); } code.push_str("\n];\n"); fs::write(&dest, code)?; Ok(()) }