Skip to main content

fey_font/
lib.rs

1//! Font loading and glyph rasterization.
2
3use ab_glyph::InvalidFont;
4use ab_glyph::{Font as AbFont, FontRef, FontVec, ScaleFont};
5use fey_color::GreyAlpha8;
6use fey_grid::GridMut;
7use fey_img::{Image, Pixel};
8use fey_math::{Vec2, vec2};
9use std::io::BufRead;
10use std::path::Path;
11use thiserror::Error;
12
13/// A glyph ID.
14#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
15pub struct GlyphId(u16);
16
17impl GlyphId {
18    pub const NUL: Self = Self(0);
19}
20
21#[derive(Debug)]
22enum FontData<'a> {
23    Ref(FontRef<'a>),
24    Vec(FontVec),
25}
26
27/// A font file loaded from memory, with an assigned size.
28#[derive(Debug)]
29pub struct Font<'a> {
30    font: FontData<'a>,
31    size: f32,
32    pt_size: f32,
33}
34
35impl<'a> Font<'a> {
36    /// Load a font from a slice of bytes.
37    pub fn from_slice(data: &'a [u8], size: f32) -> Result<Self, FontError> {
38        let font = FontRef::try_from_slice(data)?;
39        let pt_size = (font.height_unscaled() * size) / font.units_per_em().unwrap();
40        Ok(Self {
41            font: FontData::Ref(font),
42            size,
43            pt_size,
44        })
45    }
46
47    /// Load a font from an owned vector of bytes.
48    pub fn from_vec(data: Vec<u8>, size: f32) -> Result<Self, FontError> {
49        let font = FontVec::try_from_vec(data)?;
50        let pt_size = (font.height_unscaled() * size) / font.units_per_em().unwrap();
51        Ok(Self {
52            font: FontData::Vec(font),
53            size,
54            pt_size,
55        })
56    }
57
58    /// Load a font from a reader.
59    pub fn from_read<R: BufRead>(mut r: R, size: f32) -> Result<Self, FontError> {
60        let mut data = Vec::new();
61        _ = r.read_to_end(&mut data)?;
62        Self::from_vec(data, size)
63    }
64
65    /// Load a font from a file.
66    pub fn from_file<P: AsRef<Path>>(path: P, size: f32) -> Result<Self, FontError> {
67        let data = std::fs::read(path)?;
68        Self::from_vec(data, size)
69    }
70
71    /// Size the font was loaded with.
72    pub const fn size(&self) -> f32 {
73        self.size
74    }
75
76    /// The font ascender (how high it can rise above the baseline).
77    #[inline]
78    pub fn ascent(&self) -> f32 {
79        match &self.font {
80            FontData::Ref(f) => f.as_scaled(self.pt_size).ascent(),
81            FontData::Vec(f) => f.as_scaled(self.pt_size).ascent(),
82        }
83    }
84
85    /// The font descender (how low it can drop below the baseline).
86    #[inline]
87    pub fn descent(&self) -> f32 {
88        match &self.font {
89            FontData::Ref(f) => f.as_scaled(self.pt_size).descent(),
90            FontData::Vec(f) => f.as_scaled(self.pt_size).descent(),
91        }
92    }
93
94    /// The font's height (equal to `ascent - descent`).
95    #[inline]
96    pub fn height(&self) -> f32 {
97        self.ascent() - self.descent()
98    }
99
100    /// How much space to put between printed lines.
101    #[inline]
102    pub fn line_gap(&self) -> f32 {
103        match &self.font {
104            FontData::Ref(f) => f.as_scaled(self.pt_size).line_gap(),
105            FontData::Vec(f) => f.as_scaled(self.pt_size).line_gap(),
106        }
107    }
108
109    /// How many glyphs are available in the font.
110    #[inline]
111    pub fn glyph_count(&self) -> usize {
112        match &self.font {
113            FontData::Ref(f) => f.glyph_count(),
114            FontData::Vec(f) => f.glyph_count(),
115        }
116    }
117
118    /// Get the glyph ID associated with a character.
119    #[inline]
120    pub fn char_id(&self, chr: char) -> GlyphId {
121        GlyphId(match &self.font {
122            FontData::Ref(f) => f.glyph_id(chr).0,
123            FontData::Vec(f) => f.glyph_id(chr).0,
124        })
125    }
126
127    /// Iterate through all glyph IDs in the font.
128    #[inline]
129    pub fn glyph_ids(&self) -> impl Iterator<Item = GlyphId> {
130        (0..(self.glyph_count() as u16)).map(GlyphId)
131    }
132
133    /// Collect a list of all glyph/char pairs available.
134    #[inline]
135    pub fn glyph_chars(&self) -> Vec<(GlyphId, char)> {
136        match &self.font {
137            FontData::Ref(f) => f
138                .codepoint_ids()
139                .map(|(id, chr)| (GlyphId(id.0), chr))
140                .collect(),
141            FontData::Vec(f) => f
142                .codepoint_ids()
143                .map(|(id, chr)| (GlyphId(id.0), chr))
144                .collect(),
145        }
146    }
147
148    /// Retrieve glyph data associated with the glyph ID.
149    #[inline]
150    pub fn glyph(&self, id: GlyphId) -> Glyph<'_> {
151        let glyph = ab_glyph::GlyphId(id.0).with_scale(self.pt_size);
152        Glyph { font: self, glyph }
153    }
154
155    /// Retrieve the glyph data associated with the character.
156    #[inline]
157    pub fn char_glyph(&self, chr: char) -> Glyph<'_> {
158        self.glyph(self.char_id(chr))
159    }
160
161    /// How much extra to advance the cursor when printing `right`
162    /// after having just printed a `left`.
163    #[inline]
164    pub fn kerning(&self, left: GlyphId, right: GlyphId) -> f32 {
165        let [left, right] = [left, right].map(|id| ab_glyph::GlyphId(id.0));
166        match &self.font {
167            FontData::Ref(f) => f.as_scaled(self.pt_size).kern(left, right),
168            FontData::Vec(f) => f.as_scaled(self.pt_size).kern(left, right),
169        }
170    }
171
172    /// How much extra to advance the cursor when printing `right`
173    /// after having just printed a `left`.
174    #[inline]
175    pub fn char_kerning(&self, left: char, right: char) -> f32 {
176        self.kerning(self.char_id(left), self.char_id(right))
177    }
178}
179
180/// A font glyph.
181#[derive(Debug)]
182pub struct Glyph<'a> {
183    font: &'a Font<'a>,
184    glyph: ab_glyph::Glyph,
185}
186
187impl Glyph<'_> {
188    /// The glyph's ID.
189    #[inline]
190    pub fn id(&self) -> GlyphId {
191        GlyphId(self.glyph.id.0)
192    }
193
194    /// How much to advance the cursor after printing the glyph.
195    #[inline]
196    pub fn advance(&self) -> f32 {
197        let id = self.glyph.id;
198        match &self.font.font {
199            FontData::Ref(f) => f.as_scaled(self.font.pt_size).h_advance(id),
200            FontData::Vec(f) => f.as_scaled(self.font.pt_size).h_advance(id),
201        }
202    }
203
204    /// How much to horizontally offset the glyph from the cursor position.
205    #[inline]
206    pub fn left_side_bearing(&self) -> f32 {
207        let id = self.glyph.id;
208        match &self.font.font {
209            FontData::Ref(f) => f.as_scaled(self.font.pt_size).h_side_bearing(id),
210            FontData::Vec(f) => f.as_scaled(self.font.pt_size).h_side_bearing(id),
211        }
212    }
213
214    /// Rasterize the glyph, generating an image.
215    pub fn rasterize<P: Pixel, F: FnMut(f32) -> P>(&self, mut f: F) -> Option<RasterizedGlyph<P>> {
216        let outlined = match &self.font.font {
217            FontData::Ref(f) => f
218                .as_scaled(self.font.pt_size)
219                .outline_glyph(self.glyph.clone()),
220            FontData::Vec(f) => f
221                .as_scaled(self.font.pt_size)
222                .outline_glyph(self.glyph.clone()),
223        }?;
224        let bounds = outlined.px_bounds();
225        let w = bounds.width().ceil() as u32;
226        let h = bounds.height().ceil() as u32;
227        let mut image = Image::new_vec((w, h), P::default());
228        outlined.draw(|x, y, a| {
229            image.set(x, y, f(a));
230        });
231        Some(RasterizedGlyph {
232            image,
233            offset: vec2(bounds.min.x, -bounds.min.y),
234        })
235    }
236
237    /// Rasterize the glyph, generating a greyscale-alpha image where every pixel is
238    /// either fully transparent or fully opaque white.
239    #[inline]
240    pub fn rasterize_pixelated(&self) -> Option<RasterizedGlyph<GreyAlpha8>> {
241        self.rasterize(|a| {
242            if a > 0.5 {
243                GreyAlpha8::WHITE
244            } else {
245                GreyAlpha8::TRANSPARENT
246            }
247        })
248    }
249
250    /// Rasterize the glyph, generating a smooth greyscale-alpha image.
251    #[inline]
252    pub fn rasterize_smooth(&self) -> Option<RasterizedGlyph<GreyAlpha8>> {
253        //self.rasterize(|a| GreyAlpha8::new(255, (a * 255.0) as u8))
254        self.rasterize(|a| {
255            let a = (a * 255.0) as u8;
256            GreyAlpha8::new(a, a)
257        })
258    }
259}
260
261/// A rasterized glyph with a drawing offset.
262#[derive(Debug, Clone)]
263pub struct RasterizedGlyph<P: Pixel> {
264    pub image: Image<P>,
265    pub offset: Vec2<f32>,
266}
267
268#[derive(Debug, Error)]
269pub enum FontError {
270    #[error("{0}")]
271    Invalid(#[from] InvalidFont),
272
273    #[error("{0}")]
274    Io(#[from] std::io::Error),
275}