makepad_draw/text/
font.rs1use {
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;