harfrust/hb/
ot_shape_plan.rs

1use alloc::boxed::Box;
2use core::any::Any;
3use smallvec::SmallVec;
4
5use crate::hb::common::HB_FEATURE_GLOBAL_END;
6use crate::hb::common::HB_FEATURE_GLOBAL_START;
7use crate::ShaperInstance;
8
9use super::aat::map::*;
10use super::ot_map::*;
11use super::ot_shape::*;
12use super::ot_shaper::*;
13use super::{hb_font_t, hb_mask_t, Direction, Feature, Language, Script};
14
15/// A reusable plan for shaping a text buffer.
16pub struct hb_ot_shape_plan_t {
17    pub(crate) direction: Direction,
18    pub(crate) script: Option<Script>,
19    pub(crate) language: Option<Language>,
20    pub(crate) shaper: &'static hb_ot_shaper_t,
21    pub(crate) ot_map: hb_ot_map_t,
22    pub(crate) aat_map: AatMap,
23    pub(crate) data: Option<Box<dyn Any + Send + Sync>>,
24
25    pub(crate) frac_mask: hb_mask_t,
26    pub(crate) numr_mask: hb_mask_t,
27    pub(crate) dnom_mask: hb_mask_t,
28    pub(crate) rtlm_mask: hb_mask_t,
29    pub(crate) kern_mask: hb_mask_t,
30
31    pub(crate) requested_kerning: bool,
32    pub(crate) has_frac: bool,
33    pub(crate) has_vert: bool,
34    pub(crate) has_gpos_mark: bool,
35    pub(crate) zero_marks: bool,
36    pub(crate) fallback_glyph_classes: bool,
37    pub(crate) fallback_mark_positioning: bool,
38    pub(crate) adjust_mark_positioning_when_zeroing: bool,
39
40    pub(crate) apply_gpos: bool,
41    pub(crate) apply_fallback_kern: bool,
42    pub(crate) apply_kern: bool,
43    pub(crate) apply_kerx: bool,
44    pub(crate) apply_morx: bool,
45    pub(crate) apply_trak: bool,
46
47    pub(crate) user_features: SmallVec<[Feature; 4]>,
48}
49
50impl hb_ot_shape_plan_t {
51    /// Returns a plan that can be used for shaping any buffer with the
52    /// provided properties.
53    pub fn new(
54        face: &hb_font_t,
55        direction: Direction,
56        script: Option<Script>,
57        language: Option<&Language>,
58        user_features: &[Feature],
59    ) -> Self {
60        assert_ne!(
61            direction,
62            Direction::Invalid,
63            "Direction must not be Invalid"
64        );
65        let mut planner = hb_ot_shape_planner_t::new(face, direction, script, language);
66        planner.collect_features(user_features);
67        planner.compile(user_features)
68    }
69
70    pub(crate) fn data<T: 'static>(&self) -> &T {
71        self.data.as_ref().unwrap().downcast_ref().unwrap()
72    }
73
74    /// The direction of the text.
75    pub fn direction(&self) -> Direction {
76        self.direction
77    }
78
79    /// The script of the text.
80    pub fn script(&self) -> Option<Script> {
81        self.script
82    }
83
84    /// The language of the text.
85    pub fn language(&self) -> Option<&Language> {
86        self.language.as_ref()
87    }
88}
89
90/// A key used for selecting a shape plan.
91pub struct ShapePlanKey<'a> {
92    script: Option<Script>,
93    direction: Direction,
94    language: Option<&'a Language>,
95    feature_variations: [Option<u32>; 2],
96    features: &'a [Feature],
97}
98
99impl<'a> ShapePlanKey<'a> {
100    /// Creates a new shape plan key with the given script and direction.
101    pub fn new(script: Option<Script>, direction: Direction) -> Self {
102        Self {
103            script,
104            direction,
105            language: None,
106            feature_variations: [None; 2],
107            features: &[],
108        }
109    }
110
111    /// Sets the language to use for this shape plan key.
112    pub fn language(mut self, language: Option<&'a Language>) -> Self {
113        self.language = language;
114        self
115    }
116
117    /// Sets the instance to use for this shape plan key.
118    pub fn instance(mut self, instance: Option<&ShaperInstance>) -> Self {
119        self.feature_variations = instance
120            .map(|instance| instance.feature_variations)
121            .unwrap_or_default();
122        self
123    }
124
125    /// Sets the features to use for this shape plan key.
126    pub fn features(mut self, features: &'a [Feature]) -> Self {
127        self.features = features;
128        self
129    }
130
131    /// Returns true if this key is a match for the given shape plan.
132    pub fn matches(&self, plan: &hb_ot_shape_plan_t) -> bool {
133        self.script == plan.script
134            && self.direction == plan.direction
135            && self.language == plan.language.as_ref()
136            && self.feature_variations == *plan.ot_map.feature_variations()
137            && features_equivalent(self.features, &plan.user_features)
138    }
139}
140
141fn features_equivalent(features_a: &[Feature], features_b: &[Feature]) -> bool {
142    if features_a.len() != features_b.len() {
143        return false;
144    }
145    for (a, b) in features_a.iter().zip(features_b) {
146        if a.tag != b.tag
147            || a.value != b.value
148            || (a.start == HB_FEATURE_GLOBAL_START && a.end == HB_FEATURE_GLOBAL_END)
149                != (b.start == HB_FEATURE_GLOBAL_START && b.end == HB_FEATURE_GLOBAL_END)
150        {
151            return false;
152        }
153    }
154    true
155}
156
157#[cfg(test)]
158mod tests {
159    use super::hb_ot_shape_plan_t;
160
161    #[test]
162    fn test_shape_plan_is_send_and_sync() {
163        fn ensure_send_and_sync<T: Send + Sync>() {}
164        ensure_send_and_sync::<hb_ot_shape_plan_t>();
165    }
166}