1#[cfg(not(feature = "std"))]
2use core_maths::CoreFloat;
3
4use crate::hb::paint_extents::hb_paint_extents_context_t;
5use ttf_parser::gdef::GlyphClass;
6use ttf_parser::opentype_layout::LayoutTable;
7use ttf_parser::{GlyphId, RgbaColor};
8
9use super::buffer::GlyphPropsFlags;
10use super::ot_layout::TableIndex;
11use super::ot_layout_common::{PositioningTable, SubstitutionTable};
12use crate::Variation;
13
14const WINDOWS_SYMBOL_ENCODING: u16 = 0;
16const WINDOWS_UNICODE_BMP_ENCODING: u16 = 1;
17const WINDOWS_UNICODE_FULL_ENCODING: u16 = 10;
18
19const UNICODE_1_0_ENCODING: u16 = 0;
21const UNICODE_1_1_ENCODING: u16 = 1;
22const UNICODE_ISO_ENCODING: u16 = 2;
23const UNICODE_2_0_BMP_ENCODING: u16 = 3;
24const UNICODE_2_0_FULL_ENCODING: u16 = 4;
25const UNICODE_FULL_ENCODING: u16 = 6;
27
28#[derive(Clone)]
30pub struct hb_font_t<'a> {
31 pub(crate) ttfp_face: ttf_parser::Face<'a>,
32 pub(crate) units_per_em: u16,
33 pixels_per_em: Option<(u16, u16)>,
34 pub(crate) points_per_em: Option<f32>,
35 prefered_cmap_encoding_subtable: Option<u16>,
36 pub(crate) gsub: Option<SubstitutionTable<'a>>,
37 pub(crate) gpos: Option<PositioningTable<'a>>,
38}
39
40impl<'a> AsRef<ttf_parser::Face<'a>> for hb_font_t<'a> {
41 #[inline]
42 fn as_ref(&self) -> &ttf_parser::Face<'a> {
43 &self.ttfp_face
44 }
45}
46
47impl<'a> AsMut<ttf_parser::Face<'a>> for hb_font_t<'a> {
48 #[inline]
49 fn as_mut(&mut self) -> &mut ttf_parser::Face<'a> {
50 &mut self.ttfp_face
51 }
52}
53
54impl<'a> core::ops::Deref for hb_font_t<'a> {
55 type Target = ttf_parser::Face<'a>;
56
57 #[inline]
58 fn deref(&self) -> &Self::Target {
59 &self.ttfp_face
60 }
61}
62
63impl<'a> core::ops::DerefMut for hb_font_t<'a> {
64 #[inline]
65 fn deref_mut(&mut self) -> &mut Self::Target {
66 &mut self.ttfp_face
67 }
68}
69
70impl<'a> hb_font_t<'a> {
71 pub fn from_slice(data: &'a [u8], face_index: u32) -> Option<Self> {
75 let face = ttf_parser::Face::parse(data, face_index).ok()?;
76 Some(Self::from_face(face))
77 }
78
79 pub fn from_face(face: ttf_parser::Face<'a>) -> Self {
83 hb_font_t {
84 units_per_em: face.units_per_em(),
85 pixels_per_em: None,
86 points_per_em: None,
87 prefered_cmap_encoding_subtable: find_best_cmap_subtable(&face),
88 gsub: face.tables().gsub.map(SubstitutionTable::new),
89 gpos: face.tables().gpos.map(PositioningTable::new),
90 ttfp_face: face,
91 }
92 }
93
94 #[inline]
97 pub fn units_per_em(&self) -> i32 {
98 self.units_per_em as i32
99 }
100
101 #[inline]
102 pub(crate) fn pixels_per_em(&self) -> Option<(u16, u16)> {
103 self.pixels_per_em
104 }
105
106 #[inline]
112 pub fn set_pixels_per_em(&mut self, ppem: Option<(u16, u16)>) {
113 self.pixels_per_em = ppem;
114 }
115
116 #[inline]
122 pub fn set_points_per_em(&mut self, ptem: Option<f32>) {
123 self.points_per_em = ptem;
124 }
125
126 pub fn set_variations(&mut self, variations: &[Variation]) {
128 for variation in variations {
129 self.set_variation(variation.tag, variation.value);
130 }
131 }
132
133 pub(crate) fn has_glyph(&self, c: u32) -> bool {
134 self.get_nominal_glyph(c).is_some()
135 }
136
137 pub(crate) fn get_nominal_glyph(&self, mut c: u32) -> Option<GlyphId> {
138 let subtable_idx = self.prefered_cmap_encoding_subtable?;
139 let subtable = self.tables().cmap?.subtables.get(subtable_idx)?;
140
141 if subtable.platform_id == ttf_parser::PlatformId::Macintosh && c > 0x7F {
142 c = unicode_to_macroman(c);
143 }
144
145 match subtable.glyph_index(c) {
146 Some(gid) => Some(gid),
147 None => {
148 if subtable.platform_id == ttf_parser::PlatformId::Windows
151 && subtable.encoding_id == WINDOWS_SYMBOL_ENCODING
152 {
153 if c <= 0x00FF {
154 return self.get_nominal_glyph(0xF000 + c);
160 }
161 }
162
163 None
164 }
165 }
166 }
167
168 pub(crate) fn glyph_h_advance(&self, glyph: GlyphId) -> i32 {
169 self.glyph_advance(glyph, false) as i32
170 }
171
172 pub(crate) fn glyph_v_advance(&self, glyph: GlyphId) -> i32 {
173 -(self.glyph_advance(glyph, true) as i32)
174 }
175
176 fn glyph_advance(&self, glyph: GlyphId, is_vertical: bool) -> u32 {
177 let face = &self.ttfp_face;
178 if face.is_variable()
179 && face.has_non_default_variation_coordinates()
180 && face.tables().hvar.is_none()
181 && face.tables().vvar.is_none()
182 && face.glyph_phantom_points(glyph).is_none()
183 {
184 return match face.glyph_bounding_box(glyph) {
185 Some(bbox) => {
186 (if is_vertical {
187 bbox.y_max + bbox.y_min
188 } else {
189 bbox.x_max + bbox.x_min
190 }) as u32
191 }
192 None => 0,
193 };
194 }
195
196 if is_vertical {
197 if face.tables().vmtx.is_some() {
198 face.glyph_ver_advance(glyph).unwrap_or(0) as u32
199 } else {
200 (face.ascender() - face.descender()) as u32
202 }
203 } else if !is_vertical && face.tables().hmtx.is_some() {
204 face.glyph_hor_advance(glyph).unwrap_or(0) as u32
205 } else {
206 face.units_per_em() as u32
207 }
208 }
209
210 pub(crate) fn glyph_h_origin(&self, glyph: GlyphId) -> i32 {
211 self.glyph_h_advance(glyph) / 2
212 }
213
214 pub(crate) fn glyph_v_origin(&self, glyph: GlyphId) -> i32 {
215 match self.ttfp_face.glyph_y_origin(glyph) {
216 Some(y) => i32::from(y),
217 None => {
218 let mut extents = hb_glyph_extents_t::default();
219 if self.glyph_extents(glyph, &mut extents) {
220 if self.ttfp_face.tables().vmtx.is_some() {
221 extents.y_bearing + self.glyph_side_bearing(glyph, true)
222 } else {
223 let advance = self.ttfp_face.ascender() - self.ttfp_face.descender();
224 let diff = advance as i32 - -extents.height;
225 extents.y_bearing + (diff >> 1)
226 }
227 } else {
228 self.ttfp_face.ascender() as i32
230 }
231 }
232 }
233 }
234
235 pub(crate) fn glyph_side_bearing(&self, glyph: GlyphId, is_vertical: bool) -> i32 {
236 let face = &self.ttfp_face;
237 if face.is_variable() && face.tables().hvar.is_none() && face.tables().vvar.is_none() {
238 return match face.glyph_bounding_box(glyph) {
239 Some(bbox) => (if is_vertical { bbox.x_min } else { bbox.y_min }) as i32,
240 None => 0,
241 };
242 }
243
244 if is_vertical {
245 face.glyph_ver_side_bearing(glyph).unwrap_or(0) as i32
246 } else {
247 face.glyph_hor_side_bearing(glyph).unwrap_or(0) as i32
248 }
249 }
250
251 pub(crate) fn glyph_extents(
252 &self,
253 glyph: GlyphId,
254 glyph_extents: &mut hb_glyph_extents_t,
255 ) -> bool {
256 let pixels_per_em = self.pixels_per_em.map_or(u16::MAX, |ppem| ppem.0);
257
258 if let Some(img) = self.ttfp_face.glyph_raster_image(glyph, pixels_per_em) {
259 if img.format == ttf_parser::RasterImageFormat::PNG {
261 let scale = self.units_per_em as f32 / img.pixels_per_em as f32;
262 glyph_extents.x_bearing = (f32::from(img.x) * scale).round() as i32;
263 glyph_extents.y_bearing =
264 ((f32::from(img.y) + f32::from(img.height)) * scale).round() as i32;
265 glyph_extents.width = (f32::from(img.width) * scale).round() as i32;
266 glyph_extents.height = (-f32::from(img.height) * scale).round() as i32;
267 return true;
268 }
269 } else if let Some(colr) = self.ttfp_face.tables().colr {
273 if colr.is_simple() {
274 return false;
275 }
276
277 if let Some(clip_box) = colr.clip_box(glyph, self.variation_coordinates()) {
278 glyph_extents.x_bearing = (clip_box.x_min).round() as i32;
280 glyph_extents.y_bearing = (clip_box.y_max).round() as i32;
281 glyph_extents.width = (clip_box.x_max - clip_box.x_min).round() as i32;
282 glyph_extents.height = (clip_box.y_min - clip_box.y_max).round() as i32;
283 return true;
284 }
285
286 let mut extents_data = hb_paint_extents_context_t::new(&self.ttfp_face);
287 let ret = colr
288 .paint(
289 glyph,
290 0,
291 &mut extents_data,
292 self.variation_coordinates(),
293 RgbaColor::new(0, 0, 0, 0),
294 )
295 .is_some();
296
297 let e = extents_data.get_extents();
298 if e.is_void() {
299 glyph_extents.x_bearing = 0;
300 glyph_extents.y_bearing = 0;
301 glyph_extents.width = 0;
302 glyph_extents.height = 0;
303 } else {
304 glyph_extents.x_bearing = e.x_min as i32;
305 glyph_extents.y_bearing = e.y_max as i32;
306 glyph_extents.width = (e.x_max - e.x_min) as i32;
307 glyph_extents.height = (e.y_min - e.y_max) as i32;
308 }
309
310 return ret;
311 }
312
313 let mut bbox = None;
314
315 if let Some(glyf) = self.ttfp_face.tables().glyf {
316 bbox = glyf.bbox(glyph);
317 }
318
319 if self.ttfp_face.tables().glyf.is_some() && bbox.is_none() {
321 return true;
323 }
324
325 let Some(bbox) = bbox else {
326 return false;
327 };
328
329 glyph_extents.x_bearing = i32::from(bbox.x_min);
330 glyph_extents.y_bearing = i32::from(bbox.y_max);
331 glyph_extents.width = i32::from(bbox.width());
332 glyph_extents.height = i32::from(bbox.y_min - bbox.y_max);
333
334 true
335 }
336
337 pub(crate) fn glyph_name(&self, glyph: GlyphId) -> Option<&str> {
338 self.ttfp_face.glyph_name(glyph)
339 }
340
341 pub(crate) fn glyph_props(&self, glyph: GlyphId) -> u16 {
342 let table = match self.tables().gdef {
343 Some(v) => v,
344 None => return 0,
345 };
346
347 match table.glyph_class(glyph) {
348 Some(GlyphClass::Base) => GlyphPropsFlags::BASE_GLYPH.bits(),
349 Some(GlyphClass::Ligature) => GlyphPropsFlags::LIGATURE.bits(),
350 Some(GlyphClass::Mark) => {
351 let class = table.glyph_mark_attachment_class(glyph);
352 (class << 8) | GlyphPropsFlags::MARK.bits()
353 }
354 _ => 0,
355 }
356 }
357
358 pub(crate) fn layout_table(&self, table_index: TableIndex) -> Option<&LayoutTable<'a>> {
359 match table_index {
360 TableIndex::GSUB => self.gsub.as_ref().map(|table| &table.inner),
361 TableIndex::GPOS => self.gpos.as_ref().map(|table| &table.inner),
362 }
363 }
364
365 pub(crate) fn layout_tables(
366 &self,
367 ) -> impl Iterator<Item = (TableIndex, &LayoutTable<'a>)> + '_ {
368 TableIndex::iter().filter_map(move |idx| self.layout_table(idx).map(|table| (idx, table)))
369 }
370}
371
372#[derive(Clone, Copy, Default)]
373#[repr(C)]
374pub struct hb_glyph_extents_t {
375 pub x_bearing: i32,
376 pub y_bearing: i32,
377 pub width: i32,
378 pub height: i32,
379}
380
381unsafe impl bytemuck::Zeroable for hb_glyph_extents_t {}
382unsafe impl bytemuck::Pod for hb_glyph_extents_t {}
383
384fn find_best_cmap_subtable(face: &ttf_parser::Face) -> Option<u16> {
385 use ttf_parser::PlatformId;
386
387 find_cmap_subtable(face, PlatformId::Windows, WINDOWS_SYMBOL_ENCODING)
391 .or_else(|| find_cmap_subtable(face, PlatformId::Windows, WINDOWS_UNICODE_FULL_ENCODING))
393 .or_else(|| find_cmap_subtable(face, PlatformId::Unicode, UNICODE_FULL_ENCODING))
394 .or_else(|| find_cmap_subtable(face, PlatformId::Unicode, UNICODE_2_0_FULL_ENCODING))
395 .or_else(|| find_cmap_subtable(face, PlatformId::Windows, WINDOWS_UNICODE_BMP_ENCODING))
397 .or_else(|| find_cmap_subtable(face, PlatformId::Unicode, UNICODE_2_0_BMP_ENCODING))
398 .or_else(|| find_cmap_subtable(face, PlatformId::Unicode, UNICODE_ISO_ENCODING))
399 .or_else(|| find_cmap_subtable(face, PlatformId::Unicode, UNICODE_1_1_ENCODING))
400 .or_else(|| find_cmap_subtable(face, PlatformId::Unicode, UNICODE_1_0_ENCODING))
401 .or_else(|| find_cmap_subtable(face, PlatformId::Macintosh, 0))
403}
404
405fn find_cmap_subtable(
406 face: &ttf_parser::Face,
407 platform_id: ttf_parser::PlatformId,
408 encoding_id: u16,
409) -> Option<u16> {
410 for (i, subtable) in face.tables().cmap?.subtables.into_iter().enumerate() {
411 if subtable.platform_id == platform_id && subtable.encoding_id == encoding_id {
412 return Some(i as u16);
413 }
414 }
415
416 None
417}
418
419#[rustfmt::skip]
420static UNICODE_TO_MACROMAN: &[u16] = &[
421 0x00C4, 0x00C5, 0x00C7, 0x00C9, 0x00D1, 0x00D6, 0x00DC, 0x00E1,
422 0x00E0, 0x00E2, 0x00E4, 0x00E3, 0x00E5, 0x00E7, 0x00E9, 0x00E8,
423 0x00EA, 0x00EB, 0x00ED, 0x00EC, 0x00EE, 0x00EF, 0x00F1, 0x00F3,
424 0x00F2, 0x00F4, 0x00F6, 0x00F5, 0x00FA, 0x00F9, 0x00FB, 0x00FC,
425 0x2020, 0x00B0, 0x00A2, 0x00A3, 0x00A7, 0x2022, 0x00B6, 0x00DF,
426 0x00AE, 0x00A9, 0x2122, 0x00B4, 0x00A8, 0x2260, 0x00C6, 0x00D8,
427 0x221E, 0x00B1, 0x2264, 0x2265, 0x00A5, 0x00B5, 0x2202, 0x2211,
428 0x220F, 0x03C0, 0x222B, 0x00AA, 0x00BA, 0x03A9, 0x00E6, 0x00F8,
429 0x00BF, 0x00A1, 0x00AC, 0x221A, 0x0192, 0x2248, 0x2206, 0x00AB,
430 0x00BB, 0x2026, 0x00A0, 0x00C0, 0x00C3, 0x00D5, 0x0152, 0x0153,
431 0x2013, 0x2014, 0x201C, 0x201D, 0x2018, 0x2019, 0x00F7, 0x25CA,
432 0x00FF, 0x0178, 0x2044, 0x20AC, 0x2039, 0x203A, 0xFB01, 0xFB02,
433 0x2021, 0x00B7, 0x201A, 0x201E, 0x2030, 0x00C2, 0x00CA, 0x00C1,
434 0x00CB, 0x00C8, 0x00CD, 0x00CE, 0x00CF, 0x00CC, 0x00D3, 0x00D4,
435 0xF8FF, 0x00D2, 0x00DA, 0x00DB, 0x00D9, 0x0131, 0x02C6, 0x02DC,
436 0x00AF, 0x02D8, 0x02D9, 0x02DA, 0x00B8, 0x02DD, 0x02DB, 0x02C7,
437];
438
439fn unicode_to_macroman(c: u32) -> u32 {
440 let u = c as u16;
441 let Some(index) = UNICODE_TO_MACROMAN.iter().position(|m| *m == u) else {
442 return 0;
443 };
444 (0x80 + index) as u32
445}