harfrust/hb/
ot_shape_plan.rs1use 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
15pub 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
50pub trait AnyFont {
51 fn with_font<F, R>(&self, f: F) -> R
52 where
53 F: FnOnce(Option<&hb_font_t>) -> R;
54}
55
56impl AnyFont for hb_font_t<'_> {
57 fn with_font<F, R>(&self, f: F) -> R
58 where
59 F: FnOnce(Option<&hb_font_t>) -> R,
60 {
61 f(Some(self))
62 }
63}
64
65impl AnyFont for crate::font::FontInstance {
66 fn with_font<F, R>(&self, f: F) -> R
67 where
68 F: FnOnce(Option<&hb_font_t>) -> R,
69 {
70 let hb_font = hb_font_t::from_font(self);
71 f(hb_font.as_ref())
72 }
73}
74
75impl hb_ot_shape_plan_t {
76 pub fn new(
79 font: &impl AnyFont,
80 direction: Direction,
81 script: Option<Script>,
82 language: Option<&Language>,
83 user_features: &[Feature],
84 ) -> Self {
85 assert_ne!(
86 direction,
87 Direction::Invalid,
88 "Direction must not be Invalid"
89 );
90 font.with_font(|font| {
91 let font = font.expect("font should be available for shaping");
92 let mut planner = hb_ot_shape_planner_t::new(font, direction, script, language);
93 planner.collect_features(user_features);
94 planner.compile(user_features)
95 })
96 }
97
98 pub(crate) fn data<T: 'static>(&self) -> &T {
99 self.data.as_ref().unwrap().downcast_ref().unwrap()
100 }
101
102 pub fn direction(&self) -> Direction {
104 self.direction
105 }
106
107 pub fn script(&self) -> Option<Script> {
109 self.script
110 }
111
112 pub fn language(&self) -> Option<&Language> {
114 self.language.as_ref()
115 }
116}
117
118pub struct ShapePlanKey<'a> {
120 script: Option<Script>,
121 direction: Direction,
122 language: Option<&'a Language>,
123 feature_variations: [Option<u32>; 2],
124 features: &'a [Feature],
125}
126
127impl<'a> ShapePlanKey<'a> {
128 pub fn new(script: Option<Script>, direction: Direction) -> Self {
130 Self {
131 script,
132 direction,
133 language: None,
134 feature_variations: [None; 2],
135 features: &[],
136 }
137 }
138
139 pub fn language(mut self, language: Option<&'a Language>) -> Self {
141 self.language = language;
142 self
143 }
144
145 pub fn instance(mut self, instance: Option<&ShaperInstance>) -> Self {
147 self.feature_variations = instance
148 .map(|instance| instance.feature_variations)
149 .unwrap_or_default();
150 self
151 }
152
153 pub fn features(mut self, features: &'a [Feature]) -> Self {
155 self.features = features;
156 self
157 }
158
159 pub fn matches(&self, plan: &hb_ot_shape_plan_t) -> bool {
161 self.script == plan.script
162 && self.direction == plan.direction
163 && self.language == plan.language.as_ref()
164 && self.feature_variations == *plan.ot_map.feature_variations()
165 && features_equivalent(self.features, &plan.user_features)
166 }
167}
168
169fn features_equivalent(features_a: &[Feature], features_b: &[Feature]) -> bool {
170 if features_a.len() != features_b.len() {
171 return false;
172 }
173 for (a, b) in features_a.iter().zip(features_b) {
174 if a.tag != b.tag
175 || a.value != b.value
176 || (a.start == HB_FEATURE_GLOBAL_START && a.end == HB_FEATURE_GLOBAL_END)
177 != (b.start == HB_FEATURE_GLOBAL_START && b.end == HB_FEATURE_GLOBAL_END)
178 {
179 return false;
180 }
181 }
182 true
183}
184
185#[cfg(test)]
186mod tests {
187 use super::hb_ot_shape_plan_t;
188
189 #[test]
190 fn test_shape_plan_is_send_and_sync() {
191 fn ensure_send_and_sync<T: Send + Sync>() {}
192 ensure_send_and_sync::<hb_ot_shape_plan_t>();
193 }
194}