use std::iter::repeat;
#[derive(Debug, thiserror::Error)]
pub enum Error {
#[error("I/O {0}")]
Io(#[from] std::io::Error),
#[error("Expected property '{0}'")]
Expected(&'static str),
#[error("Unknown font format")]
UnknownFormat(),
}
pub(crate) type Result<T> = std::result::Result<T, Error>;
pub struct Bitmap {
pub(crate) height: u8,
pub(crate) width: u8,
bmap: Vec<u8>,
}
pub(crate) struct PixIter<'a> {
bmap: &'a Bitmap,
pos: usize,
}
pub enum Prop<'a> {
Unknown(&'a str),
FontName(&'a str),
FontNumber(u8),
FontHeight(u8),
FontWidth(u8),
CharSpacing(u8),
LineSpacing(u8),
Baseline(u8),
MaxCharNumber(u16),
CodePoint(u16),
Bitmap(Bitmap),
}
impl<'a> Iterator for PixIter<'a> {
type Item = bool;
fn next(&mut self) -> Option<bool> {
let pos = self.pos;
let len = usize::from(self.bmap.height) * usize::from(self.bmap.width);
if pos < len {
self.pos += 1;
let off = pos >> 3;
let bit = 7 - (pos & 0b111);
Some((self.bmap.bmap[off] >> bit) & 1 != 0)
} else {
None
}
}
}
impl Bitmap {
pub(crate) fn new(width: u8) -> Self {
Bitmap {
height: 0,
width,
bmap: Vec::with_capacity(32),
}
}
pub fn from_bits(height: u8, width: u8, bmap: Vec<u8>) -> Option<Self> {
let len = usize::from(height) * usize::from(width);
if bmap.len() == (len + 7) / 8 {
Some(Bitmap {
height,
width,
bmap,
})
} else {
None
}
}
pub fn height(&self) -> u8 {
self.height
}
pub fn width(&self) -> u8 {
self.width
}
pub(crate) fn push_row(&mut self, row: impl Iterator<Item = bool>) {
let width = usize::from(self.width);
let mut pos = usize::from(self.height) * width;
for pix in row.chain(repeat(false)).take(width) {
if pos & 0b111 == 0 {
self.bmap.push(0);
}
if pix {
let off = pos >> 3;
let bit = 7 - (pos & 0b111);
self.bmap[off] |= 1 << bit;
}
pos += 1;
}
self.height += 1;
}
pub(crate) fn pixels(&self) -> impl Iterator<Item = bool> + '_ {
PixIter { bmap: self, pos: 0 }
}
pub fn into_bits(self) -> Vec<u8> {
self.bmap
}
}
impl<'a> Prop<'a> {
pub fn font_name(&self) -> Option<&'a str> {
match self {
Prop::FontName(nm) => Some(nm),
_ => None,
}
}
pub fn font_number(&self) -> Option<u8> {
match self {
Prop::FontNumber(num) => Some(*num),
_ => None,
}
}
pub fn char_spacing(&self) -> Option<u8> {
match self {
Prop::CharSpacing(cs) => Some(*cs),
_ => None,
}
}
pub fn line_spacing(&self) -> Option<u8> {
match self {
Prop::LineSpacing(ls) => Some(*ls),
_ => None,
}
}
pub fn font_height(&self) -> Option<u8> {
match self {
Prop::FontHeight(fh) => Some(*fh),
Prop::Bitmap(bmap) => Some(bmap.height),
_ => None,
}
}
pub fn code_point(&self) -> Option<u16> {
match self {
Prop::CodePoint(cp) => Some(*cp),
_ => None,
}
}
}