os_terminal/font/
truetype.rs1use 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}