1use ttf_parser::Face;
4
5use crate::dim::Dim;
6use crate::error::{Error, FontError};
7
8pub const STIX_TWO_MATH_OTF: &[u8] =
10 include_bytes!("../../fonts/stix-two-math/STIXTwoMath-Regular.otf");
11
12pub const STIX_TWO_MATH_SHA256: &str =
14 "f2076b9f1676438439dd41e23676f5ab99056e83d6b8f8c27841591ef2ccfa72";
15
16pub const STIX_TWO_MATH_NAME: &str = "STIX Two Math";
18
19#[derive(Clone, Debug, PartialEq, Eq)]
32pub struct GlyphMetrics {
33 pub ch: char,
35 pub glyph_id: u16,
37 pub advance_fu: u16,
39 pub advance: Dim,
41 pub height: Dim,
43 pub depth: Dim,
45}
46
47pub struct MathFont {
58 raw: &'static [u8],
59 face: Face<'static>,
60 units_per_em: u16,
61 ascender_fu: i16,
62 descender_fu: i16,
63}
64
65impl MathFont {
66 pub fn stix_two_math() -> Result<Self, Error> {
79 Self::from_bytes(STIX_TWO_MATH_OTF)
80 }
81
82 pub fn from_bytes(raw: &'static [u8]) -> Result<Self, Error> {
85 let face = Face::parse(raw, 0).map_err(|_| FontError::InvalidFace)?;
86 let units_per_em = face.units_per_em();
87 if units_per_em == 0 {
88 return Err(FontError::InvalidFace.into());
89 }
90 let ascender_fu = face.ascender();
91 let descender_fu = face.descender();
92 Ok(Self {
93 raw,
94 face,
95 units_per_em,
96 ascender_fu,
97 descender_fu,
98 })
99 }
100
101 pub(crate) fn face(&self) -> &Face<'static> {
102 &self.face
103 }
104
105 #[must_use]
107 pub fn bytes(&self) -> &'static [u8] {
108 self.raw
109 }
110
111 #[must_use]
113 pub fn units_per_em(&self) -> u16 {
114 self.units_per_em
115 }
116
117 #[must_use]
119 pub fn ascender_fu(&self) -> i16 {
120 self.ascender_fu
121 }
122
123 #[must_use]
125 pub fn descender_fu(&self) -> i16 {
126 self.descender_fu
127 }
128
129 #[must_use]
131 pub fn ascender(&self) -> Dim {
132 Dim::from_font_units(i64::from(self.ascender_fu), self.units_per_em)
133 }
134
135 #[must_use]
137 pub fn descender(&self) -> Dim {
138 let d = i64::from(self.descender_fu);
139 Dim::from_font_units(-d, self.units_per_em)
140 }
141
142 pub fn glyph(&self, ch: char) -> Result<GlyphMetrics, Error> {
144 let face = self.face();
145 let gid = face.glyph_index(ch).ok_or(FontError::MissingGlyph { ch })?;
146 let advance_fu = face
147 .glyph_hor_advance(gid)
148 .ok_or(FontError::MissingGlyph { ch })?;
149 let mut height_fu = 0i64;
150 let mut depth_fu = 0i64;
151 if let Some(bbox) = face.glyph_bounding_box(gid) {
152 height_fu = i64::from(bbox.y_max).max(0);
153 depth_fu = i64::from(-bbox.y_min).max(0);
154 }
155 let upem = self.units_per_em;
156 Ok(GlyphMetrics {
157 ch,
158 glyph_id: gid.0,
159 advance_fu,
160 advance: Dim::from_font_units(i64::from(advance_fu), upem),
161 height: Dim::from_font_units(height_fu, upem),
162 depth: Dim::from_font_units(depth_fu, upem),
163 })
164 }
165
166 pub fn glyph_id(&self, ch: char, gid: u16) -> Result<GlyphMetrics, Error> {
168 let face = self.face();
169 let gid = ttf_parser::GlyphId(gid);
170 let advance_fu = face
171 .glyph_hor_advance(gid)
172 .ok_or(FontError::MissingGlyph { ch })?;
173 let mut height_fu = 0i64;
174 let mut depth_fu = 0i64;
175 if let Some(bbox) = face.glyph_bounding_box(gid) {
176 height_fu = i64::from(bbox.y_max).max(0);
177 depth_fu = i64::from(-bbox.y_min).max(0);
178 }
179 let upem = self.units_per_em;
180 Ok(GlyphMetrics {
181 ch,
182 glyph_id: gid.0,
183 advance_fu,
184 advance: Dim::from_font_units(i64::from(advance_fu), upem),
185 height: Dim::from_font_units(height_fu, upem),
186 depth: Dim::from_font_units(depth_fu, upem),
187 })
188 }
189
190 pub fn italic_correction(&self, glyph_id: u16) -> Dim {
192 let face = self.face();
193 let Some(math) = face.tables().math else {
194 return Dim::zero();
195 };
196 let Some(info) = math.glyph_info else {
197 return Dim::zero();
198 };
199 let Some(table) = info.italic_corrections else {
200 return Dim::zero();
201 };
202 match table.get(ttf_parser::GlyphId(glyph_id)) {
203 Some(v) => Dim::from_font_units(i64::from(v.value), self.units_per_em),
204 None => Dim::zero(),
205 }
206 }
207
208 pub fn top_accent_attachment(&self, glyph_id: u16) -> Option<Dim> {
210 let face = self.face();
211 let math = face.tables().math?;
212 let info = math.glyph_info?;
213 let table = info.top_accent_attachments?;
214 let v = table.get(ttf_parser::GlyphId(glyph_id))?;
215 Some(Dim::from_font_units(i64::from(v.value), self.units_per_em))
216 }
217
218 pub fn horizontal_assembly_parts(&self, glyph_id: u16) -> Vec<(u16, u16, u16, u16, bool)> {
221 let mut out = Vec::new();
222 let face = self.face();
223 let Some(math) = face.tables().math else {
224 return out;
225 };
226 let Some(variants) = math.variants else {
227 return out;
228 };
229 let Some(cons) = variants
230 .horizontal_constructions
231 .get(ttf_parser::GlyphId(glyph_id))
232 else {
233 return out;
234 };
235 let Some(assembly) = cons.assembly else {
236 return out;
237 };
238 for i in 0..assembly.parts.len() {
239 if let Some(p) = assembly.parts.get(i) {
240 out.push((
241 p.glyph_id.0,
242 p.start_connector_length,
243 p.end_connector_length,
244 p.full_advance,
245 p.part_flags.extender(),
246 ));
247 }
248 }
249 out
250 }
251
252 pub fn horizontal_variants(&self, glyph_id: u16) -> Vec<u16> {
254 let mut out = vec![glyph_id];
255 let face = self.face();
256 let Some(math) = face.tables().math else {
257 return out;
258 };
259 let Some(variants) = math.variants else {
260 return out;
261 };
262 let Some(cons) = variants
263 .horizontal_constructions
264 .get(ttf_parser::GlyphId(glyph_id))
265 else {
266 return out;
267 };
268 for i in 0..cons.variants.len() {
269 if let Some(v) = cons.variants.get(i) {
270 out.push(v.variant_glyph.0);
271 }
272 }
273 out
274 }
275
276 pub fn vertical_variants(&self, glyph_id: u16) -> Vec<u16> {
278 let mut out = vec![glyph_id];
279 let face = self.face();
280 let Some(math) = face.tables().math else {
281 return out;
282 };
283 let Some(variants) = math.variants else {
284 return out;
285 };
286 let Some(cons) = variants
287 .vertical_constructions
288 .get(ttf_parser::GlyphId(glyph_id))
289 else {
290 return out;
291 };
292 for i in 0..cons.variants.len() {
293 if let Some(v) = cons.variants.get(i) {
294 out.push(v.variant_glyph.0);
295 }
296 }
297 out
298 }
299
300 #[must_use]
302 pub fn sha256_hex(bytes: &[u8]) -> String {
303 let d = zenith_float::sha256(bytes);
304 let mut s = String::with_capacity(64);
305 for b in d {
306 s.push_str(&hex_byte(b));
307 }
308 s
309 }
310}
311
312fn hex_byte(b: u8) -> String {
313 const H: &[u8; 16] = b"0123456789abcdef";
314 let hi = H[(b >> 4) as usize];
315 let lo = H[(b & 0xf) as usize];
316 let mut out = String::with_capacity(2);
317 out.push(hi as char);
318 out.push(lo as char);
319 out
320}