os_terminal/font/
truetype.rs

1use ab_glyph::{Font, FontRef, PxScale, ScaleFont, VariableFont};
2use alloc::{collections::BTreeMap, vec::Vec};
3
4use super::{ContentInfo, FontManager, Rasterized};
5
6pub struct TrueTypeFont {
7    font: FontRef<'static>,
8    italic_font: Option<FontRef<'static>>,
9    raster_height: usize,
10    raster_width: usize,
11    font_size: PxScale,
12    base_line_offset: f32,
13    bitmap_cache: BTreeMap<ContentInfo, Vec<Vec<u8>>>,
14}
15
16impl TrueTypeFont {
17    pub fn new(font_size: f32, font_bytes: &'static [u8]) -> Self {
18        let font = FontRef::try_from_slice(font_bytes).unwrap();
19        let font_size = font.pt_to_px_scale(font_size).unwrap();
20        let scaled_font = font.as_scaled(font_size);
21
22        let line_height = scaled_font.height();
23        let base_line_offset = scaled_font.ascent();
24
25        Self {
26            font,
27            italic_font: None,
28            raster_height: line_height as usize,
29            raster_width: (line_height / 2.0) as usize,
30            font_size,
31            base_line_offset,
32            bitmap_cache: BTreeMap::new(),
33        }
34    }
35
36    pub fn with_italic_font(mut self, italic_font: &'static [u8]) -> Self {
37        self.italic_font = Some(FontRef::try_from_slice(italic_font).unwrap());
38        self
39    }
40}
41
42impl FontManager for TrueTypeFont {
43    fn size(&self) -> (usize, usize) {
44        (self.raster_width, self.raster_height)
45    }
46
47    fn rasterize(&mut self, info: ContentInfo) -> Rasterized {
48        Rasterized::Vec(self.bitmap_cache.entry(info.clone()).or_insert_with(|| {
49            let select_font = self
50                .italic_font
51                .as_mut()
52                .filter(|_| info.italic)
53                .unwrap_or(&mut self.font);
54
55            let font_weight = if info.bold { 700.0 } else { 400.0 };
56            select_font.set_variation(b"wght", font_weight);
57
58            let glyph_id = select_font.glyph_id(info.content);
59            let glyph = glyph_id.with_scale(self.font_size);
60
61            let actual_width = self.raster_width * if info.wide { 2 } else { 1 };
62            let mut letter_bitmap = vec![vec![0u8; actual_width]; self.raster_height];
63
64            if let Some(bitmap) = select_font.outline_glyph(glyph) {
65                let px_bounds = bitmap.px_bounds();
66
67                let x_offset = px_bounds.min.x as isize;
68                let y_offset = (self.base_line_offset + px_bounds.min.y) as isize;
69
70                bitmap.draw(|x, y, c| {
71                    let x = x_offset + x as isize;
72                    let y = y_offset + y as isize;
73
74                    if (0..actual_width as isize).contains(&x)
75                        && (0..self.raster_height as isize).contains(&y)
76                    {
77                        letter_bitmap[y as usize][x as usize] = (c * 255.0) as u8;
78                    }
79                });
80            }
81
82            letter_bitmap
83        }))
84    }
85}