Skip to main content

rocketsplash_rt/font/
cls_font.rs

1// <FILE>crates/rocketsplash-rt/src/font/cls_font.rs</FILE>
2// <DESC>Font runtime representation and API</DESC>
3// <VERS>VERSION: 1.0.1</VERS>
4// <WCTX>Runtime library implementation</WCTX>
5// <CLOG>Refine has_style to match available variant semantics</CLOG>
6
7use std::collections::BTreeMap;
8use std::path::Path;
9
10use rocketsplash_formats::{FontMeta, RenderMode, StyleFlags};
11
12use crate::font::load_font_from_bytes;
13use crate::{Error, TextBuilder, TextStyle};
14
15#[derive(Clone, Debug)]
16pub struct Font {
17    pub meta: FontMeta,
18    pub line_height: u32,
19    pub mode: RenderMode,
20    pub available_styles: StyleFlags,
21    pub(crate) glyphs: BTreeMap<char, RuntimeGlyphVariants>,
22}
23
24impl Font {
25    pub fn load(path: impl AsRef<Path>) -> Result<Self, Error> {
26        let bytes = std::fs::read(path)?;
27        load_font_from_bytes(&bytes)
28    }
29
30    pub fn from_bytes(bytes: &[u8]) -> Result<Self, Error> {
31        load_font_from_bytes(bytes)
32    }
33
34    pub fn render<'a>(&'a self, text: &str) -> TextBuilder<'a> {
35        TextBuilder::new(self, text)
36    }
37
38    pub fn available_chars(&self) -> impl Iterator<Item = char> + '_ {
39        self.glyphs.keys().copied()
40    }
41
42    pub fn has_glyph(&self, ch: char) -> bool {
43        self.glyphs.contains_key(&ch)
44    }
45
46    pub fn has_style(&self, style: TextStyle) -> bool {
47        let bold = style.contains(TextStyle::BOLD);
48        let italic = style.contains(TextStyle::ITALIC);
49        if bold && italic && !self.available_styles.contains(StyleFlags::BOLD_ITALIC) {
50            return false;
51        }
52        if bold && !italic && !self.available_styles.contains(StyleFlags::BOLD) {
53            return false;
54        }
55        if italic && !bold && !self.available_styles.contains(StyleFlags::ITALIC) {
56            return false;
57        }
58        if style.contains(TextStyle::REVERSE)
59            && !self.available_styles.contains(StyleFlags::REVERSE)
60        {
61            return false;
62        }
63        true
64    }
65
66    pub fn mode(&self) -> RenderMode {
67        self.mode
68    }
69
70    pub fn line_height(&self) -> usize {
71        self.line_height as usize
72    }
73}
74
75#[derive(Clone, Debug)]
76pub(crate) struct RuntimeGlyph {
77    pub width: usize,
78    pub height: usize,
79    pub chars: Vec<char>,
80    pub opacity: Option<Vec<u8>>,
81}
82
83impl RuntimeGlyph {
84    pub fn char_at(&self, x: usize, y: usize) -> Option<char> {
85        if x >= self.width || y >= self.height {
86            return None;
87        }
88        let idx = y * self.width + x;
89        self.chars.get(idx).copied()
90    }
91}
92
93#[derive(Clone, Debug)]
94pub(crate) struct RuntimeGlyphVariants {
95    pub base: RuntimeGlyph,
96    pub bold: Option<RuntimeGlyph>,
97    pub italic: Option<RuntimeGlyph>,
98    pub bold_italic: Option<RuntimeGlyph>,
99    pub reverse: Option<RuntimeGlyph>,
100}
101
102impl RuntimeGlyphVariants {
103    pub fn select(&self, style: TextStyle) -> &RuntimeGlyph {
104        if style.contains(TextStyle::REVERSE) {
105            if let Some(glyph) = &self.reverse {
106                return glyph;
107            }
108        }
109        let bold = style.contains(TextStyle::BOLD);
110        let italic = style.contains(TextStyle::ITALIC);
111        if bold && italic {
112            if let Some(glyph) = &self.bold_italic {
113                return glyph;
114            }
115        }
116        if bold {
117            if let Some(glyph) = &self.bold {
118                return glyph;
119            }
120        }
121        if italic {
122            if let Some(glyph) = &self.italic {
123                return glyph;
124            }
125        }
126        &self.base
127    }
128}
129
130// <FILE>crates/rocketsplash-rt/src/font/cls_font.rs</FILE>
131// <VERS>END OF VERSION: 1.0.1</VERS>