makepad_draw/text/
font.rs

1use {
2    super::{
3        font_face::FontFace,
4        geom::{Point, Rect},
5        glyph_outline,
6        glyph_outline::GlyphOutline,
7        glyph_raster_image::GlyphRasterImage,
8        intern::Intern,
9        rasterizer::{RasterizedGlyph, Rasterizer},
10    },
11    makepad_rustybuzz as rustybuzz,
12    rustybuzz::ttf_parser,
13    std::{
14        cell::RefCell,
15        collections::HashMap,
16        hash::{Hash, Hasher},
17        rc::Rc,
18    },
19};
20
21#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
22pub struct FontId(u64);
23
24impl From<u64> for FontId {
25    fn from(value: u64) -> Self {
26        Self(value)
27    }
28}
29
30impl From<&str> for FontId {
31    fn from(value: &str) -> Self {
32        Self(value.intern().as_ptr() as u64)
33    }
34}
35
36#[derive(Debug)]
37pub struct Font {
38    id: FontId,
39    rasterizer: Rc<RefCell<Rasterizer>>,
40    face: FontFace,
41    ascender_fudge_in_ems: f32,
42    descender_fudge_in_ems: f32,
43    cached_glyph_outline_bounds_in_ems: RefCell<HashMap<GlyphId, Option<Rect<f32>>>>,
44}
45
46impl Font {
47    pub fn new(
48        id: FontId,
49        rasterizer: Rc<RefCell<Rasterizer>>,
50        face: FontFace,
51        ascender_fudge_in_ems: f32,
52        descender_fudge_in_ems: f32,
53    ) -> Self {
54        Self {
55            id,
56            rasterizer,
57            face,
58            ascender_fudge_in_ems,
59            descender_fudge_in_ems,
60            cached_glyph_outline_bounds_in_ems: RefCell::new(HashMap::new()),
61        }
62    }
63
64    pub fn id(&self) -> FontId {
65        self.id
66    }
67
68    pub(super) fn ttf_parser_face(&self) -> &ttf_parser::Face<'_> {
69        self.face.as_ttf_parser_face()
70    }
71
72    pub(super) fn rustybuzz_face(&self) -> &rustybuzz::Face<'_> {
73        self.face.as_rustybuzz_face()
74    }
75
76    pub fn units_per_em(&self) -> f32 {
77        self.ttf_parser_face().units_per_em() as f32
78    }
79
80    pub fn ascender_in_ems(&self) -> f32 {
81        self.ttf_parser_face().ascender() as f32 / self.units_per_em() + self.ascender_fudge_in_ems
82    }
83
84    pub fn descender_in_ems(&self) -> f32 {
85        self.ttf_parser_face().descender() as f32 / self.units_per_em()
86            + self.descender_fudge_in_ems
87    }
88
89    pub fn line_gap_in_ems(&self) -> f32 {
90        self.ttf_parser_face().line_gap() as f32 / self.units_per_em()
91    }
92
93    pub fn glyph_outline(&self, glyph_id: GlyphId) -> Option<GlyphOutline> {
94        let face = self.ttf_parser_face();
95        let glyph_id = ttf_parser::GlyphId(glyph_id);
96        let mut builder = glyph_outline::Builder::new();
97        let bounds = face.outline_glyph(glyph_id, &mut builder)?;
98        let min = Point::new(bounds.x_min as f32, bounds.y_min as f32);
99        let max = Point::new(bounds.x_max as f32, bounds.y_max as f32);
100        Some(builder.finish(Rect::new(min, max - min), self.units_per_em()))
101    }
102
103    pub fn glyph_outline_bounds_in_ems(
104        &self,
105        glyph_id: GlyphId,
106        out_outline: &mut Option<GlyphOutline>,
107    ) -> Option<Rect<f32>> {
108        if let Some(bounds_in_ems) = self
109            .cached_glyph_outline_bounds_in_ems
110            .borrow()
111            .get(&glyph_id)
112        {
113            return *bounds_in_ems;
114        }
115        if let Some(outline) = self.glyph_outline(glyph_id) {
116            let bounds_in_ems = outline.bounds_in_ems();
117            *out_outline = Some(outline);
118            self.cached_glyph_outline_bounds_in_ems
119                .borrow_mut()
120                .insert(glyph_id, Some(bounds_in_ems));
121            Some(bounds_in_ems)
122        } else {
123            None
124        }
125    }
126
127    pub fn glyph_raster_image(
128        &self,
129        glyph_id: GlyphId,
130        dpxs_per_em: f32,
131    ) -> Option<GlyphRasterImage<'_>> {
132        let face = self.ttf_parser_face();
133        let glyph_id = ttf_parser::GlyphId(glyph_id);
134        let image = face.glyph_raster_image(glyph_id, dpxs_per_em as u16)?;
135        GlyphRasterImage::from_raster_glyph_image(image)
136    }
137
138    pub fn rasterize_glyph(&self, glyph_id: GlyphId, dpxs_per_em: f32) -> Option<RasterizedGlyph> {
139        self.rasterizer
140            .borrow_mut()
141            .rasterize_glyph(self, glyph_id, dpxs_per_em)
142    }
143}
144
145impl Eq for Font {}
146
147impl Hash for Font {
148    fn hash<H: Hasher>(&self, state: &mut H) {
149        self.id.hash(state);
150    }
151}
152
153impl PartialEq for Font {
154    fn eq(&self, other: &Self) -> bool {
155        self.id == other.id
156    }
157}
158
159pub type GlyphId = u16;