harfrust/hb/
face.rs

1use read_fonts::types::{F2Dot14, Fixed, GlyphId};
2use read_fonts::{FontRef, TableProvider};
3use smallvec::SmallVec;
4
5use super::aat::AatTables;
6use super::charmap::{cache_t as cmap_cache_t, Charmap};
7use super::glyph_metrics::GlyphMetrics;
8use super::glyph_names::GlyphNames;
9use super::ot::{LayoutTable, OtCache, OtTables};
10use super::ot_layout::TableIndex;
11use super::ot_shape::{hb_ot_shape_context_t, shape_internal};
12use crate::hb::aat::AatCache;
13use crate::hb::buffer::hb_buffer_t;
14use crate::hb::tables::TableOffsets;
15use crate::{script, Feature, GlyphBuffer, NormalizedCoord, ShapePlan, UnicodeBuffer, Variation};
16
17/// Data required for shaping with a single font.
18pub struct ShaperData {
19    table_offsets: TableOffsets,
20    ot_cache: OtCache,
21    aat_cache: AatCache,
22    cmap_cache: cmap_cache_t,
23}
24
25impl ShaperData {
26    /// Creates new cached shaper data for the given font.
27    pub fn new(font: &FontRef) -> Self {
28        let ot_cache = OtCache::new(font);
29        let aat_cache = AatCache::new(font);
30        let table_offsets = TableOffsets::new(font);
31        let cmap_cache = cmap_cache_t::new();
32        Self {
33            table_offsets,
34            ot_cache,
35            aat_cache,
36            cmap_cache,
37        }
38    }
39
40    /// Returns a builder for constructing a new shaper with the given
41    /// font.
42    pub fn shaper<'a>(&'a self, font: &FontRef<'a>) -> ShaperBuilder<'a> {
43        ShaperBuilder {
44            data: self,
45            font: font.clone(),
46            instance: None,
47            point_size: None,
48        }
49    }
50}
51
52// Maximum number of coordinates to store inline before spilling to the
53// heap.
54//
55// Any value between 5 and 11 yields a SmallVec footprint of 32 bytes.
56const MAX_INLINE_COORDS: usize = 11;
57
58/// An instance of a variable font.
59#[derive(Clone, Default, Debug)]
60pub struct ShaperInstance {
61    coords: SmallVec<[F2Dot14; MAX_INLINE_COORDS]>,
62    pub(crate) feature_variations: [Option<u32>; 2],
63    // TODO: this is a good place to hang variation specific caches
64}
65
66impl ShaperInstance {
67    /// Creates a new shaper instance for the given font from the specified
68    /// list of variation settings.
69    ///
70    /// The setting values are in user space and the order is insignificant.
71    pub fn from_variations<V>(font: &FontRef, variations: V) -> Self
72    where
73        V: IntoIterator,
74        V::Item: Into<Variation>,
75    {
76        let mut this = Self::default();
77        this.set_variations(font, variations);
78        this
79    }
80
81    /// Creates a new shaper instance for the given font from the specified
82    /// set of normalized coordinates.
83    ///
84    /// The sequence of coordinates is expected to be in axis order.
85    pub fn from_coords(font: &FontRef, coords: impl IntoIterator<Item = NormalizedCoord>) -> Self {
86        let mut this = Self::default();
87        this.set_coords(font, coords);
88        this
89    }
90
91    /// Creates a new shaper instance for the given font using the variation
92    /// position from the named instance at the specified index.
93    pub fn from_named_instance(font: &FontRef, index: usize) -> Self {
94        let mut this = Self::default();
95        this.set_named_instance(font, index);
96        this
97    }
98
99    /// Returns the underlying set of normalized coordinates.
100    pub fn coords(&self) -> &[F2Dot14] {
101        &self.coords
102    }
103
104    /// Resets the instance for the given font and variation settings.
105    pub fn set_variations<V>(&mut self, font: &FontRef, variations: V)
106    where
107        V: IntoIterator,
108        V::Item: Into<Variation>,
109    {
110        self.coords.clear();
111        if let Ok(fvar) = font.fvar() {
112            self.coords
113                .resize(fvar.axis_count() as usize, F2Dot14::ZERO);
114            fvar.user_to_normalized(
115                font.avar().ok().as_ref(),
116                variations
117                    .into_iter()
118                    .map(Into::into)
119                    .map(|var| (var.tag, Fixed::from_f64(var.value as _))),
120                self.coords.as_mut_slice(),
121            );
122            self.check_default();
123            self.set_feature_variations(font);
124        }
125    }
126
127    /// Resets the instance for the given font and normalized coordinates.
128    pub fn set_coords(&mut self, font: &FontRef, coords: impl IntoIterator<Item = F2Dot14>) {
129        self.coords.clear();
130        if let Ok(fvar) = font.fvar() {
131            let count = fvar.axis_count() as usize;
132            self.coords.reserve(count);
133            self.coords.extend(coords.into_iter().take(count));
134            self.check_default();
135            self.set_feature_variations(font);
136        }
137    }
138
139    /// Resets the instance for the given font using the variation
140    /// position from the named instance at the specified index.
141    pub fn set_named_instance(&mut self, font: &FontRef, index: usize) {
142        self.coords.clear();
143        if let Ok(fvar) = font.fvar() {
144            if let Ok((axes, instance)) = fvar
145                .axis_instance_arrays()
146                .and_then(|arrays| Ok((arrays.axes(), arrays.instances().get(index)?)))
147            {
148                self.set_variations(
149                    font,
150                    axes.iter()
151                        .zip(instance.coordinates)
152                        .map(|(axis, coord)| (axis.axis_tag(), coord.get().to_f32())),
153                );
154            }
155        }
156    }
157
158    fn set_feature_variations(&mut self, font: &FontRef) {
159        self.feature_variations = [None; 2];
160        if self.coords.is_empty() {
161            return;
162        }
163        self.feature_variations[0] = font
164            .gsub()
165            .ok()
166            .and_then(|t| LayoutTable::Gsub(t).feature_variation_index(&self.coords));
167        self.feature_variations[1] = font
168            .gpos()
169            .ok()
170            .and_then(|t| LayoutTable::Gpos(t).feature_variation_index(&self.coords));
171    }
172
173    fn check_default(&mut self) {
174        if self.coords.iter().all(|coord| *coord == F2Dot14::ZERO) {
175            self.coords.clear();
176        }
177    }
178}
179
180/// Builder type for constructing a [`Shaper`](crate::Shaper).
181pub struct ShaperBuilder<'a> {
182    data: &'a ShaperData,
183    font: FontRef<'a>,
184    instance: Option<&'a ShaperInstance>,
185    point_size: Option<f32>,
186}
187
188impl<'a> ShaperBuilder<'a> {
189    /// Sets an optional instance for the shaper.
190    ///
191    /// This defines the variable font configuration.
192    pub fn instance(mut self, instance: Option<&'a ShaperInstance>) -> Self {
193        self.instance = instance;
194        self
195    }
196
197    /// Sets the point size for the shaper.
198    ///
199    /// This controls adjustments provided by the tracking table.
200    pub fn point_size(mut self, size: Option<f32>) -> Self {
201        self.point_size = size;
202        self
203    }
204
205    /// Builds the shaper with the current configuration.
206    pub fn build(self) -> crate::Shaper<'a> {
207        let font = self.font;
208        let units_per_em = self.data.table_offsets.units_per_em;
209        let charmap = Charmap::new(&font, &self.data.table_offsets, &self.data.cmap_cache);
210        let glyph_metrics = GlyphMetrics::new(&font, &self.data.table_offsets);
211        let (coords, feature_variations) = self
212            .instance
213            .map(|instance| (instance.coords(), instance.feature_variations))
214            .unwrap_or_default();
215        let ot_tables = OtTables::new(
216            &font,
217            &self.data.ot_cache,
218            &self.data.table_offsets,
219            coords,
220            feature_variations,
221        );
222        let aat_tables = AatTables::new(&font, &self.data.aat_cache, &self.data.table_offsets);
223        hb_font_t {
224            font,
225            units_per_em,
226            points_per_em: self.point_size,
227            charmap,
228            glyph_metrics,
229            ot_tables,
230            aat_tables,
231        }
232    }
233}
234
235/// A configured shaper.
236#[derive(Clone)]
237pub struct hb_font_t<'a> {
238    pub(crate) font: FontRef<'a>,
239    pub(crate) units_per_em: u16,
240    pub(crate) points_per_em: Option<f32>,
241    charmap: Charmap<'a>,
242    glyph_metrics: GlyphMetrics<'a>,
243    pub(crate) ot_tables: OtTables<'a>,
244    pub(crate) aat_tables: AatTables<'a>,
245}
246
247impl<'a> crate::Shaper<'a> {
248    /// Returns font's units per EM.
249    #[inline]
250    pub fn units_per_em(&self) -> i32 {
251        self.units_per_em as i32
252    }
253
254    /// Returns the currently active normalized coordinates.
255    pub fn coords(&self) -> &'a [NormalizedCoord] {
256        self.ot_tables.coords
257    }
258
259    /// Shapes the buffer content using provided font and features.
260    ///
261    /// Consumes the buffer. You can then run [`GlyphBuffer::clear`] to get the [`UnicodeBuffer`] back
262    /// without allocating a new one.
263    ///
264    /// If you plan to shape multiple strings, prefer [`shape_with_plan`](Self::shape_with_plan).
265    /// This is because [`ShapePlan`](crate::ShapePlan) initialization is pretty slow and should preferably
266    /// be called once for each shaping configuration.
267    pub fn shape(&self, buffer: UnicodeBuffer, features: &[Feature]) -> GlyphBuffer {
268        let plan = ShapePlan::new(
269            self,
270            buffer.0.direction,
271            buffer.0.script,
272            buffer.0.language.as_ref(),
273            features,
274        );
275        self.shape_with_plan(&plan, buffer, features)
276    }
277
278    /// Shapes the buffer content using the provided font and plan.
279    ///
280    /// Consumes the buffer. You can then run [`GlyphBuffer::clear`] to get the [`UnicodeBuffer`] back
281    /// without allocating a new one.
282    ///
283    /// It is up to the caller to ensure that the shape plan matches the properties of the provided
284    /// buffer, otherwise the shaping result will likely be incorrect.
285    ///
286    /// # Panics
287    ///
288    /// Will panic when debugging assertions are enabled if the buffer and plan have mismatched
289    /// properties.
290    pub fn shape_with_plan(
291        &self,
292        plan: &ShapePlan,
293        buffer: UnicodeBuffer,
294        features: &[Feature],
295    ) -> GlyphBuffer {
296        let mut buffer = buffer.0;
297        buffer.enter();
298
299        assert_eq!(
300            buffer.direction, plan.direction,
301            "Buffer direction does not match plan direction: {:?} != {:?}",
302            buffer.direction, plan.direction
303        );
304        assert_eq!(
305            buffer.script.unwrap_or(script::UNKNOWN),
306            plan.script.unwrap_or(script::UNKNOWN),
307            "Buffer script does not match plan script: {:?} != {:?}",
308            buffer.script.unwrap_or(script::UNKNOWN),
309            plan.script.unwrap_or(script::UNKNOWN)
310        );
311
312        if buffer.len > 0 {
313            // Save the original direction, we use it later.
314            let target_direction = buffer.direction;
315            shape_internal(&mut hb_ot_shape_context_t {
316                plan,
317                face: self,
318                buffer: &mut buffer,
319                target_direction,
320                features,
321            });
322        }
323
324        buffer.leave();
325
326        GlyphBuffer(buffer)
327    }
328
329    pub(crate) fn has_glyph(&self, c: u32) -> bool {
330        self.get_nominal_glyph(c).is_some()
331    }
332
333    pub(crate) fn get_nominal_glyph(&self, c: u32) -> Option<GlyphId> {
334        self.charmap.map(c)
335    }
336
337    pub(crate) fn get_nominal_variant_glyph(&self, c: u32, vs: u32) -> Option<GlyphId> {
338        self.charmap.map_variant(c, vs)
339    }
340
341    pub(crate) fn glyph_h_advance(&self, glyph: GlyphId) -> i32 {
342        self.glyph_metrics
343            .advance_width(glyph, self.ot_tables.coords)
344            .unwrap_or_default()
345    }
346    pub(crate) fn glyph_h_advances(&self, buffer: &mut hb_buffer_t) {
347        self.glyph_metrics
348            .populate_advance_widths(buffer, self.ot_tables.coords);
349    }
350
351    pub(crate) fn glyph_v_advance(&self, glyph: GlyphId) -> i32 {
352        -self
353            .glyph_metrics
354            .advance_height(glyph, self.ot_tables.coords)
355            .unwrap_or(self.units_per_em as i32)
356    }
357
358    pub(crate) fn glyph_h_origin(&self, glyph: GlyphId) -> i32 {
359        self.glyph_h_advance(glyph) / 2
360    }
361
362    pub(crate) fn glyph_v_origin(&self, glyph: GlyphId) -> i32 {
363        self.glyph_metrics
364            .v_origin(glyph, self.ot_tables.coords)
365            .unwrap_or_default()
366    }
367
368    pub(crate) fn glyph_extents(
369        &self,
370        glyph: GlyphId,
371        glyph_extents: &mut hb_glyph_extents_t,
372    ) -> bool {
373        if let Some(extents) = self.glyph_metrics.extents(glyph, self.ot_tables.coords) {
374            glyph_extents.x_bearing = extents.x_min;
375            glyph_extents.y_bearing = extents.y_max;
376            glyph_extents.width = extents.x_max - extents.x_min;
377            glyph_extents.height = extents.y_min - extents.y_max;
378            true
379        } else {
380            false
381        }
382    }
383
384    pub(crate) fn glyph_names(&self) -> GlyphNames<'a> {
385        GlyphNames::new(&self.font)
386    }
387
388    pub(crate) fn layout_table(&self, table_index: TableIndex) -> Option<LayoutTable<'a>> {
389        match table_index {
390            TableIndex::GSUB => self
391                .ot_tables
392                .gsub
393                .as_ref()
394                .map(|table| LayoutTable::Gsub(table.table.clone())),
395            TableIndex::GPOS => self
396                .ot_tables
397                .gpos
398                .as_ref()
399                .map(|table| LayoutTable::Gpos(table.table.clone())),
400        }
401    }
402
403    pub(crate) fn layout_tables(&self) -> impl Iterator<Item = (TableIndex, LayoutTable<'a>)> + '_ {
404        TableIndex::iter().filter_map(move |idx| self.layout_table(idx).map(|table| (idx, table)))
405    }
406}
407
408#[derive(Clone, Copy, Default, bytemuck::Pod, bytemuck::Zeroable)]
409#[repr(C)]
410pub struct hb_glyph_extents_t {
411    pub x_bearing: i32,
412    pub y_bearing: i32,
413    pub width: i32,
414    pub height: i32,
415}