tfon/
common.rs

1// common.rs
2//
3use std::iter::repeat;
4
5/// Font parse/write error
6#[derive(Debug, thiserror::Error)]
7pub enum Error {
8    #[error("I/O {0}")]
9    Io(#[from] std::io::Error),
10
11    #[error("Expected property '{0}'")]
12    Expected(&'static str),
13
14    #[error("Unknown font format")]
15    UnknownFormat(),
16}
17
18/// Result type
19pub(crate) type Result<T> = std::result::Result<T, Error>;
20
21/// Bitmap of pixels
22pub struct Bitmap {
23    /// Height in pixels
24    pub(crate) height: u8,
25    /// Width in pixels
26    pub(crate) width: u8,
27    /// Vec of pixels
28    bmap: Vec<u8>,
29}
30
31/// Pixel iterator for bitmaps
32pub(crate) struct PixIter<'a> {
33    bmap: &'a Bitmap,
34    pos: usize,
35}
36
37/// Font property
38pub enum Prop<'a> {
39    /// Unknown property
40    Unknown(&'a str),
41    /// Font name
42    FontName(&'a str),
43    /// Font number
44    FontNumber(u8),
45    /// Font height (pixels)
46    FontHeight(u8),
47    /// Font width (pixels)
48    FontWidth(u8),
49    /// Pixel spacing between characters
50    CharSpacing(u8),
51    /// Pixel spacing between lines
52    LineSpacing(u8),
53    /// Baseline of characters
54    Baseline(u8),
55    /// Maximum character number
56    MaxCharNumber(u16),
57    /// Character code point
58    CodePoint(u16),
59    /// Character bitmap
60    Bitmap(Bitmap),
61}
62
63impl Iterator for PixIter<'_> {
64    type Item = bool;
65
66    fn next(&mut self) -> Option<bool> {
67        let pos = self.pos;
68        let len = usize::from(self.bmap.height) * usize::from(self.bmap.width);
69        if pos < len {
70            self.pos += 1;
71            let off = pos >> 3;
72            let bit = 7 - (pos & 0b111);
73            Some((self.bmap.bmap[off] >> bit) & 1 != 0)
74        } else {
75            None
76        }
77    }
78}
79
80impl Bitmap {
81    /// Create a new bitmap
82    pub(crate) fn new(width: u8) -> Self {
83        Bitmap {
84            height: 0,
85            width,
86            bmap: Vec::with_capacity(32),
87        }
88    }
89
90    /// Create a bitmap from bits
91    pub fn from_bits(height: u8, width: u8, bmap: Vec<u8>) -> Option<Self> {
92        let len = usize::from(height) * usize::from(width);
93        if bmap.len() == (len + 7) / 8 {
94            Some(Bitmap {
95                height,
96                width,
97                bmap,
98            })
99        } else {
100            None
101        }
102    }
103
104    /// Get pixel height
105    pub fn height(&self) -> u8 {
106        self.height
107    }
108
109    /// Get pixel width
110    pub fn width(&self) -> u8 {
111        self.width
112    }
113
114    /// Push a row of pixels to the bitmap
115    pub(crate) fn push_row(&mut self, row: impl Iterator<Item = bool>) {
116        let width = usize::from(self.width);
117        let mut pos = usize::from(self.height) * width;
118        for pix in row.chain(repeat(false)).take(width) {
119            if pos & 0b111 == 0 {
120                self.bmap.push(0);
121            }
122            if pix {
123                let off = pos >> 3;
124                let bit = 7 - (pos & 0b111);
125                self.bmap[off] |= 1 << bit;
126            }
127            pos += 1;
128        }
129        self.height += 1;
130    }
131
132    /// Get an iterator of all pixels
133    pub(crate) fn pixels(&self) -> impl Iterator<Item = bool> + '_ {
134        PixIter { bmap: self, pos: 0 }
135    }
136
137    /// Convert into a Vec of packed bits
138    pub fn into_bits(self) -> Vec<u8> {
139        self.bmap
140    }
141}
142
143impl<'a> Prop<'a> {
144    /// Get font name
145    pub fn font_name(&self) -> Option<&'a str> {
146        match self {
147            Prop::FontName(nm) => Some(nm),
148            _ => None,
149        }
150    }
151
152    /// Get font number
153    pub fn font_number(&self) -> Option<u8> {
154        match self {
155            Prop::FontNumber(num) => Some(*num),
156            _ => None,
157        }
158    }
159
160    /// Get character spacing
161    pub fn char_spacing(&self) -> Option<u8> {
162        match self {
163            Prop::CharSpacing(cs) => Some(*cs),
164            _ => None,
165        }
166    }
167
168    /// Get line spacing
169    pub fn line_spacing(&self) -> Option<u8> {
170        match self {
171            Prop::LineSpacing(ls) => Some(*ls),
172            _ => None,
173        }
174    }
175
176    /// Get font height
177    pub fn font_height(&self) -> Option<u8> {
178        match self {
179            Prop::FontHeight(fh) => Some(*fh),
180            Prop::Bitmap(bmap) => Some(bmap.height),
181            _ => None,
182        }
183    }
184
185    /// Get code point
186    pub fn code_point(&self) -> Option<u16> {
187        match self {
188            Prop::CodePoint(cp) => Some(*cp),
189            _ => None,
190        }
191    }
192}