use byteorder::{ReadBytesExt, BigEndian};
const UNIFONT_DATA: &[u8] = include_bytes!("unifont.dat");
pub const MAX_UNICODE_CODEPOINT: u32 = 0x10FFFF;
pub const NUM_UNICODE_CODEPOINTS: u32 = MAX_UNICODE_CODEPOINT + 1;
pub const MAX_UNICODE_PAGE: u32 = NUM_UNICODE_PAGES-1;
pub const NUM_UNICODE_PAGES: u32 = NUM_UNICODE_CODEPOINTS >> 8;
#[derive(PartialEq,Eq)]
pub struct Bitmap<'a> {
bytes: &'a [u8],
}
impl<'a> core::fmt::Debug for Bitmap<'a> {
fn fmt(&self, fmt: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> {
fmt.write_str("unifont bitmap {")?;
core::fmt::Debug::fmt(self.bytes, fmt)?;
fmt.write_str("}")
}
}
impl<'a> Bitmap<'a> {
pub fn get_bytes(&self) -> &'a [u8] { self.bytes }
pub fn is_wide(&self) -> bool {
match self.bytes.len() {
16 => false,
32 => true,
_ => unreachable!(),
}
}
pub fn get_dimensions<T: From<u8>>(&self) -> (T, T) {
match self.is_wide() {
false => (8.into(), 16.into()),
true => (16.into(), 16.into()),
}
}
}
#[derive(Default)]
struct PageInfo {
uncompressed_size: u32,
compressed_offset: u32,
raw_data: Option<Vec<u8>>,
}
pub struct Unifont {
pages: [PageInfo; NUM_UNICODE_PAGES as usize],
}
impl Unifont {
pub fn load_bitmap(&mut self, codepoint: u32) -> Bitmap {
assert!(codepoint <= MAX_UNICODE_CODEPOINT);
let page = codepoint >> 8;
self.load_page(page);
let ret = self.get_bitmap(codepoint);
if let Some(x) = ret { return unsafe { std::mem::transmute(x) } }
drop(ret);
if codepoint == 0xFFFD {
panic!("U+FFFD should have been loaded but wasn't!");
}
else {
self.load_bitmap(0xFFFD)
}
}
pub fn get_bitmap(&self, codepoint: u32) -> Option<Bitmap> {
assert!(codepoint <= MAX_UNICODE_CODEPOINT);
let page = codepoint >> 8;
let ch = codepoint & 255;
let raw_data = match self.pages[page as usize].raw_data.as_ref() {
None => return None,
Some(x) => &x[..],
};
let offset_offset = (ch as usize) * 2;
let char_offset =
u16::from_ne_bytes(raw_data[offset_offset .. offset_offset + 2]
.try_into().unwrap());
if char_offset == 0 {
if codepoint == 0xFFFD {
panic!("U+FFFD should have been present but wasn't!");
}
else {
self.get_bitmap(0xFFFD)
}
}
else {
let is_wide = (char_offset & 1) != 0;
let real_offset = (char_offset & !1) as usize;
let region = &raw_data[real_offset .. real_offset +
if is_wide { 32 } else { 16 }];
Some(Bitmap { bytes: region })
}
}
pub fn load_page(&mut self, page: u32) {
assert!(page <= MAX_UNICODE_PAGE);
let target_page = &mut self.pages[page as usize];
if target_page.raw_data.is_none() {
if target_page.uncompressed_size == 0 {
target_page.raw_data = Some(vec![0u8; 512]);
}
else {
let mut inflater = flate2::Decompress::new(true);
let mut buf = vec![0; target_page.uncompressed_size as usize];
inflater.decompress(&UNIFONT_DATA[target_page.compressed_offset as usize ..], &mut buf[..], flate2::FlushDecompress::Finish).expect("The Unifont bitmap data in this application appears to be corrupted!");
let mut running_offset = 512u16;
for n in 0 .. 256 {
let i = (n * 2) as usize;
let in_offset = u16::from_be_bytes(buf[i..i+2].try_into().unwrap());
let out_offset;
match in_offset {
0x0000 => {
out_offset = running_offset;
running_offset += 16;
},
0x0001 => {
out_offset = running_offset | 1;
running_offset += 32;
},
0x0101 => {
out_offset = 0;
},
_ => {
panic!("The Unifont bitmap data in this application appears to be corrupted!");
},
}
buf[i..i+2].copy_from_slice(&out_offset.to_ne_bytes());
}
target_page.raw_data = Some(buf)
}
}
}
pub fn open() -> Unifont {
let mut pages: [std::mem::MaybeUninit<PageInfo>;
NUM_UNICODE_PAGES as usize]
= unsafe { std::mem::MaybeUninit::uninit().assume_init() };
for el in &mut pages[..] {
unsafe { std::ptr::write(el.as_mut_ptr(), PageInfo {
compressed_offset: 0, uncompressed_size: 0, raw_data: None
}) }
}
let mut ret = Unifont { pages: unsafe { std::mem::transmute(pages) } };
ret.populate_page_infos();
ret
}
fn populate_page_infos(&mut self) {
let mut input = UNIFONT_DATA;
let start_offset: u32
= input.read_u32::<BigEndian>().unwrap() + 4;
let mut running_offset = start_offset;
let mut buf = [0u8; NUM_UNICODE_PAGES as usize * 4];
let mut fish = flate2::Decompress::new(true);
fish.decompress(&UNIFONT_DATA[4..(running_offset as usize)],
&mut buf, flate2::FlushDecompress::Finish).unwrap();
let mut i = &buf[..];
for el in &mut self.pages[..] {
let uncompressed_size = i.read_u16::<BigEndian>().unwrap();
let compressed_size = i.read_u16::<BigEndian>().unwrap();
el.uncompressed_size = uncompressed_size as u32;
if el.uncompressed_size > 0 {
el.compressed_offset = running_offset;
running_offset += compressed_size as u32;
}
else {
el.compressed_offset = 0;
}
}
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn bogus_page() {
let mut unifont = Unifont::open();
let fffd = unifont.load_bitmap(0xFFFD);
drop(fffd);
let bad = unifont.load_bitmap(0x104560);
drop(bad);
let fffd = unifont.get_bitmap(0xFFFD);
let bad = unifont.get_bitmap(0x104560);
assert_eq!(fffd, bad);
}
}