1use alloc::boxed::Box;
2use read_fonts::types::{F2Dot14, Fixed};
3use read_fonts::{FontRef, TableProvider};
4use smallvec::SmallVec;
5
6#[cfg(not(feature = "std"))]
8#[allow(unused_imports)]
9use core_maths::CoreFloat as _;
10
11use super::aat::AatTables;
12use super::charmap::{cache_t as cmap_cache_t, Charmap};
13use super::font_funcs::FontFuncsDispatch;
14use super::glyph_metrics::GlyphMetrics;
15use super::glyph_names::GlyphNames;
16use super::ot::{LayoutTable, OtCache, OtTables};
17use super::ot_layout::TableIndex;
18use super::ot_shape::OtShapeContext;
19use crate::hb::aat::AatCache;
20use crate::hb::tables::TableRanges;
21use crate::{script, Feature, GlyphBuffer, NormalizedCoord, ShapePlan, UnicodeBuffer, Variation};
22
23pub use super::font_funcs::{AdvanceWidthBatch, BuiltinFontFuncs, FontFuncs, RawAdvanceWidthBatch};
24
25pub struct ShaperData {
27 table_ranges: TableRanges,
28 ot_cache: OtCache,
29 aat_cache: AatCache,
30 cmap_cache: cmap_cache_t,
31 apply_trak: bool,
33}
34
35impl ShaperData {
36 pub fn new(font: &FontRef) -> Self {
38 let ot_cache = OtCache::new(font);
39 let aat_cache = AatCache::new(font);
40 let table_ranges = TableRanges::new(font);
41 let cmap_cache = cmap_cache_t::new();
42 let apply_trak = font.trak().is_ok() && font.stat().is_ok();
43 Self {
44 table_ranges,
45 ot_cache,
46 aat_cache,
47 cmap_cache,
48 apply_trak,
49 }
50 }
51
52 fn from_font(font: &crate::font::Font) -> Self {
53 let tables = font.tables();
54 let ot_cache = OtCache::new(&tables);
55 let aat_cache = AatCache::new(&tables);
56 let table_ranges = TableRanges::from_tables(&tables);
57 let cmap_cache = cmap_cache_t::new();
58 let apply_trak = tables.trak_data().is_some() && tables.stat_data().is_some();
59 Self {
60 table_ranges,
61 ot_cache,
62 aat_cache,
63 cmap_cache,
64 apply_trak,
65 }
66 }
67
68 pub fn shaper<'a>(&'a self, font: &FontRef<'a>) -> ShaperBuilder<'a> {
71 ShaperBuilder {
72 data: self,
73 font: font.clone(),
74 instance: None,
75 }
76 }
77}
78
79const MAX_INLINE_COORDS: usize = 11;
84
85#[derive(Clone, Default, Debug)]
87pub struct ShaperInstance {
88 coords: SmallVec<[F2Dot14; MAX_INLINE_COORDS]>,
89 pub(crate) feature_variations: [Option<u32>; 2],
90 }
92
93impl ShaperInstance {
94 pub fn from_variations<V>(font: &FontRef, variations: V) -> Self
99 where
100 V: IntoIterator,
101 V::Item: Into<Variation>,
102 {
103 let mut this = Self::default();
104 this.set_variations(font, variations);
105 this
106 }
107
108 pub fn from_coords(font: &FontRef, coords: impl IntoIterator<Item = NormalizedCoord>) -> Self {
113 let mut this = Self::default();
114 this.set_coords(font, coords);
115 this
116 }
117
118 pub fn from_named_instance(font: &FontRef, index: usize) -> Self {
121 let mut this = Self::default();
122 this.set_named_instance(font, index);
123 this
124 }
125
126 pub fn coords(&self) -> &[F2Dot14] {
128 &self.coords
129 }
130
131 pub fn set_variations<V>(&mut self, font: &FontRef, variations: V)
133 where
134 V: IntoIterator,
135 V::Item: Into<Variation>,
136 {
137 self.coords.clear();
138 if let Ok(fvar) = font.fvar() {
139 self.coords
140 .resize(fvar.axis_count() as usize, F2Dot14::ZERO);
141 fvar.user_to_normalized(
142 font.avar().ok().as_ref(),
143 variations
144 .into_iter()
145 .map(Into::into)
146 .map(|var| (var.tag, Fixed::from_f64(var.value as _))),
147 self.coords.as_mut_slice(),
148 );
149 self.check_default();
150 self.set_feature_variations(font);
151 }
152 }
153
154 pub fn set_coords(&mut self, font: &FontRef, coords: impl IntoIterator<Item = F2Dot14>) {
156 self.coords.clear();
157 if let Ok(fvar) = font.fvar() {
158 let count = fvar.axis_count() as usize;
159 self.coords.reserve(count);
160 self.coords.extend(coords.into_iter().take(count));
161 self.check_default();
162 self.set_feature_variations(font);
163 }
164 }
165
166 pub fn set_named_instance(&mut self, font: &FontRef, index: usize) {
169 self.coords.clear();
170 if let Ok(fvar) = font.fvar() {
171 if let Ok((axes, instance)) = fvar
172 .axis_instance_arrays()
173 .and_then(|arrays| Ok((arrays.axes(), arrays.instances().get(index)?)))
174 {
175 self.set_variations(
176 font,
177 axes.iter()
178 .zip(instance.coordinates)
179 .map(|(axis, coord)| (axis.axis_tag(), coord.get().to_f32())),
180 );
181 }
182 }
183 }
184
185 fn set_feature_variations(&mut self, font: &FontRef) {
186 self.feature_variations = [None; 2];
187 if self.coords.is_empty() {
188 return;
189 }
190 self.feature_variations[0] = font
191 .gsub()
192 .ok()
193 .and_then(|t| LayoutTable::Gsub(t).feature_variation_index(&self.coords));
194 self.feature_variations[1] = font
195 .gpos()
196 .ok()
197 .and_then(|t| LayoutTable::Gpos(t).feature_variation_index(&self.coords));
198 }
199
200 fn check_default(&mut self) {
201 if self.coords.iter().all(|coord| *coord == F2Dot14::ZERO) {
202 self.coords.clear();
203 }
204 }
205}
206
207pub struct ShaperBuilder<'a> {
209 data: &'a ShaperData,
210 font: FontRef<'a>,
211 instance: Option<&'a ShaperInstance>,
212}
213
214impl<'a> ShaperBuilder<'a> {
215 pub fn instance(mut self, instance: Option<&'a ShaperInstance>) -> Self {
219 self.instance = instance;
220 self
221 }
222
223 pub fn build(self) -> crate::Shaper<'a> {
225 let font = self.font;
226 let units_per_em = self.data.table_ranges.units_per_em;
227 let charmap = Charmap::new(&font, &self.data.table_ranges);
228 let glyph_metrics = GlyphMetrics::new(&font, &self.data.table_ranges);
229 let (coords, feature_variations) = self
230 .instance
231 .map(|instance| (instance.coords(), instance.feature_variations))
232 .unwrap_or_default();
233 let ot_tables = OtTables::new(
234 &font,
235 &self.data.ot_cache,
236 &self.data.table_ranges,
237 coords,
238 feature_variations,
239 );
240 let aat_tables = AatTables::new(&font, &self.data.aat_cache, &self.data.table_ranges);
241 let font = FontKind::FontRef(FontRefData {
242 font,
243 glyph_metrics,
244 charmap,
245 });
246 hb_font_t {
247 font,
248 units_per_em,
249 cmap_cache: &self.data.cmap_cache,
250 ot_tables,
251 aat_tables,
252 apply_trak: self.data.apply_trak,
253 }
254 }
255}
256
257#[derive(Default)]
259pub struct ShapeOptions<'a> {
260 plan: Option<&'a ShapePlan>,
261 scale: Option<(i32, i32)>,
262 point_size: Option<f32>,
263 features: &'a [Feature],
264 font_funcs: Option<&'a mut (dyn FontFuncs + 'a)>,
265}
266
267impl<'a> ShapeOptions<'a> {
268 pub fn new() -> Self {
270 Self::default()
271 }
272
273 pub fn plan(mut self, plan: Option<&'a ShapePlan>) -> Self {
278 self.plan = plan;
279 self
280 }
281
282 pub fn scale(mut self, scale: Option<i32>) -> Self {
305 self.scale = scale.map(|s| (s, s));
306 self
307 }
308
309 pub fn scale_separate(mut self, scale: Option<(i32, i32)>) -> Self {
313 self.scale = scale;
314 self
315 }
316
317 pub fn point_size(mut self, point_size: Option<f32>) -> Self {
319 self.point_size = point_size;
320 self
321 }
322
323 pub fn features(mut self, features: &'a [Feature]) -> Self {
325 self.features = features;
326 self
327 }
328
329 pub fn font_funcs(mut self, funcs: Option<&'a mut (dyn FontFuncs + 'a)>) -> Self {
331 self.font_funcs = funcs;
332 self
333 }
334}
335
336#[derive(Copy, Clone)]
337pub(crate) struct Scale {
338 x_mult: i64,
339 y_mult: i64,
340 x_multf: f32,
341 y_multf: f32,
342}
343
344impl Default for Scale {
345 fn default() -> Self {
346 Self {
347 x_mult: 1 << 16,
348 y_mult: 1 << 16,
349 x_multf: 1.0,
350 y_multf: 1.0,
351 }
352 }
353}
354
355#[allow(clippy::cast_precision_loss)]
357impl Scale {
358 pub(crate) fn new(scale: Option<(i32, i32)>, upem: i32) -> Self {
359 let (Some((x_scale, y_scale)), true) = (scale, upem != 0) else {
360 return Self::default();
363 };
364 let [x_mult, y_mult] = [x_scale, y_scale].map(|s| Self::mult_from_scale(s, upem));
365 let upem = upem as f32;
366 Self {
367 x_mult,
368 y_mult,
369 x_multf: x_scale as f32 / upem,
370 y_multf: y_scale as f32 / upem,
371 }
372 }
373
374 #[inline(always)]
375 pub(crate) fn scale_x(&self, x: i32) -> i32 {
376 Self::scale_by_mult(x, self.x_mult)
377 }
378
379 #[inline(always)]
380 pub(crate) fn scale_y(&self, y: i32) -> i32 {
381 Self::scale_by_mult(y, self.y_mult)
382 }
383
384 #[inline(always)]
387 pub(crate) fn scale_x_f(&self, x: f32) -> i32 {
388 (x * self.x_multf).round() as i32
389 }
390
391 #[inline(always)]
392 pub(crate) fn scale_y_f(&self, y: f32) -> i32 {
393 (y * self.y_multf).round() as i32
394 }
395
396 pub(crate) fn scale_extents(&self, mut extents: GlyphExtents) -> GlyphExtents {
401 let x1 = extents.x_bearing as f32 * self.x_multf;
402 let y1 = extents.y_bearing as f32 * self.y_multf;
403 let x2 = (extents.x_bearing + extents.width) as f32 * self.x_multf;
404 let y2 = (extents.y_bearing + extents.height) as f32 * self.y_multf;
405 extents.x_bearing = x1.floor() as i32;
406 extents.y_bearing = y1.floor() as i32;
407 extents.width = x2.ceil() as i32 - extents.x_bearing;
408 extents.height = y2.ceil() as i32 - extents.y_bearing;
409 extents
410 }
411
412 #[inline(always)]
413 fn mult_from_scale(scale: i32, upem: i32) -> i64 {
414 if scale < 0 {
415 -((-(scale as i64)) << 16) / upem as i64
416 } else {
417 ((scale as i64) << 16) / upem as i64
418 }
419 }
420
421 #[inline(always)]
422 fn scale_by_mult(value: i32, mult: i64) -> i32 {
423 (((value as i64) * mult + 32768) >> 16) as i32
424 }
425}
426
427pub fn shape(
440 font: &crate::font::FontInstance,
441 mut buffer: UnicodeBuffer,
442 mut options: ShapeOptions<'_>,
443) -> GlyphBuffer {
444 let Some(hb_font) = hb_font_t::from_font(font) else {
445 buffer.clear();
446 return GlyphBuffer(buffer.0);
447 };
448 if options.scale.is_none() {
451 if let Some(ppem) = font.size() {
452 options = options.scale(Some((ppem * 65536.0) as i32));
453 }
454 }
455 hb_font.shape(buffer, options)
456}
457
458#[allow(clippy::large_enum_variant)]
460#[derive(Clone)]
461pub enum FontKind<'a> {
462 FontRef(FontRefData<'a>),
463 FontInstance(&'a crate::font::FontInstance, BasicFontMetrics),
464}
465
466#[derive(Clone)]
467pub struct FontRefData<'a> {
468 pub(crate) font: FontRef<'a>,
469 pub(crate) glyph_metrics: GlyphMetrics<'a>,
470 pub(crate) charmap: Charmap<'a>,
471}
472
473#[derive(Copy, Clone, Debug)]
474pub struct BasicFontMetrics {
475 pub units_per_em: u16,
476 pub num_glyphs: u32,
477 pub ascent: i16,
478 pub descent: i16,
479}
480
481#[derive(Clone)]
483pub struct hb_font_t<'a> {
484 pub(crate) font: FontKind<'a>,
485 pub(crate) units_per_em: u16,
486 pub(crate) cmap_cache: &'a cmap_cache_t,
487 pub(crate) ot_tables: OtTables<'a>,
488 pub(crate) aat_tables: AatTables<'a>,
489 pub(crate) apply_trak: bool,
490}
491
492impl<'a> crate::Shaper<'a> {
493 pub(crate) fn from_font(font: &'a crate::font::FontInstance) -> Option<Self> {
494 let data = crate::font::_font_interop::_get_or_init_shaping_data(font, || {
495 Box::new(ShaperData::from_font(font))
496 })
497 .downcast_ref::<ShaperData>()?;
498 let metrics = BasicFontMetrics {
499 units_per_em: data.table_ranges.units_per_em,
500 num_glyphs: data.table_ranges.num_glyphs,
501 ascent: data.table_ranges.ascent,
502 descent: data.table_ranges.descent,
503 };
504 let coords = font.normalized_coords();
505 let feature_variations = font.feature_variations();
506 let feature_variations = [feature_variations.gsub(), feature_variations.gpos()];
507 let tables = font.tables();
508 let ot_tables = OtTables::from_tables(&tables, &data.ot_cache, coords, feature_variations);
509 let aat_tables = AatTables::from_tables(&tables, &ot_tables, &data.aat_cache);
510 Some(Self {
511 font: FontKind::FontInstance(font, metrics),
512 units_per_em: data.table_ranges.units_per_em,
513 cmap_cache: &data.cmap_cache,
514 ot_tables,
515 aat_tables,
516 apply_trak: data.apply_trak,
517 })
518 }
519
520 #[inline]
522 pub fn units_per_em(&self) -> i32 {
523 self.units_per_em as i32
524 }
525
526 pub fn coords(&self) -> &'a [NormalizedCoord] {
528 self.ot_tables.coords
529 }
530
531 pub fn shape(&self, buffer: UnicodeBuffer, options: ShapeOptions<'_>) -> GlyphBuffer {
544 if let Some(plan) = options.plan {
545 self.shape_with_plan(plan, buffer, options)
546 } else {
547 let plan = ShapePlan::new(
548 self,
549 buffer.0.direction,
550 buffer.0.script,
551 buffer.0.language.as_ref(),
552 options.features,
553 );
554 self.shape_with_plan(&plan, buffer, options)
555 }
556 }
557
558 fn shape_with_plan(
559 &self,
560 plan: &ShapePlan,
561 buffer: UnicodeBuffer,
562 options: ShapeOptions<'_>,
563 ) -> GlyphBuffer {
564 let mut buffer = buffer.0;
565 buffer.enter();
566
567 assert_eq!(
568 buffer.direction, plan.direction,
569 "Buffer direction does not match plan direction: {:?} != {:?}",
570 buffer.direction, plan.direction
571 );
572 assert_eq!(
573 buffer.script.unwrap_or(script::UNKNOWN),
574 plan.script.unwrap_or(script::UNKNOWN),
575 "Buffer script does not match plan script: {:?} != {:?}",
576 buffer.script.unwrap_or(script::UNKNOWN),
577 plan.script.unwrap_or(script::UNKNOWN)
578 );
579
580 if buffer.len > 0 {
581 let target_direction = buffer.direction;
583 let scale = Scale::new(options.scale, self.units_per_em as i32);
584 let mut font_funcs = FontFuncsDispatch::new(self, scale, options.font_funcs);
585 OtShapeContext {
586 plan,
587 face: self,
588 buffer: &mut buffer,
589 target_direction,
590 features: options.features,
591 point_size: options.point_size,
592 font_funcs: &mut font_funcs,
593 }
594 .shape_internal();
595 }
596
597 buffer.leave();
598
599 GlyphBuffer(buffer)
600 }
601
602 pub(crate) fn glyph_names(&self) -> GlyphNames<'a> {
603 GlyphNames::new(&self.font)
604 }
605
606 pub(crate) fn glyph_metrics(&self) -> GlyphMetrics<'a> {
607 match &self.font {
608 FontKind::FontRef(data) => data.glyph_metrics.clone(),
609 FontKind::FontInstance(instance, metrics) => {
610 GlyphMetrics::from_tables(&instance.tables(), metrics)
611 }
612 }
613 }
614
615 pub(crate) fn layout_table(&self, table_index: TableIndex) -> Option<LayoutTable<'a>> {
616 match table_index {
617 TableIndex::GSUB => self
618 .ot_tables
619 .gsub
620 .as_ref()
621 .map(|table| LayoutTable::Gsub(table.table.clone())),
622 TableIndex::GPOS => self
623 .ot_tables
624 .gpos
625 .as_ref()
626 .map(|table| LayoutTable::Gpos(table.table.clone())),
627 }
628 }
629
630 pub(crate) fn layout_tables(&self) -> impl Iterator<Item = (TableIndex, LayoutTable<'a>)> + '_ {
631 TableIndex::iter().filter_map(move |idx| self.layout_table(idx).map(|table| (idx, table)))
632 }
633}
634
635#[derive(Clone, Copy, Default, bytemuck::Pod, bytemuck::Zeroable)]
639#[repr(C)]
640pub struct GlyphExtents {
641 pub x_bearing: i32,
643 pub y_bearing: i32,
645 pub width: i32,
647 pub height: i32,
649}
650
651#[cfg(test)]
652mod tests {
653 use super::*;
654
655 #[test]
656 fn extents_scale_from_corners_like_harfbuzz() {
657 let scale = Scale::new(Some((1500, 1500)), 1000);
660 let extents = GlyphExtents {
661 x_bearing: 1,
662 y_bearing: 4,
663 width: 3,
664 height: -2,
665 };
666 let scaled = scale.scale_extents(extents);
667 assert_eq!(scaled.x_bearing, 1);
668 assert_eq!(scaled.y_bearing, 6);
669 assert_eq!(scaled.width, 5);
670 assert_eq!(scaled.height, -3);
671 }
672}