rustybuzz/hb/
ot_shaper.rs

1use alloc::boxed::Box;
2use core::any::Any;
3
4use super::buffer::*;
5use super::common::TagExt;
6use super::ot_shape::*;
7use super::ot_shape_normalize::*;
8use super::ot_shape_plan::hb_ot_shape_plan_t;
9use super::{hb_font_t, hb_tag_t, script, Direction, Script};
10
11impl hb_glyph_info_t {
12    pub(crate) fn ot_shaper_var_u8_category(&self) -> u8 {
13        let v: &[u8; 4] = bytemuck::cast_ref(&self.var2);
14        v[2]
15    }
16
17    pub(crate) fn set_ot_shaper_var_u8_category(&mut self, c: u8) {
18        let v: &mut [u8; 4] = bytemuck::cast_mut(&mut self.var2);
19        v[2] = c;
20    }
21
22    pub(crate) fn ot_shaper_var_u8_auxiliary(&self) -> u8 {
23        let v: &[u8; 4] = bytemuck::cast_ref(&self.var2);
24        v[3]
25    }
26
27    pub(crate) fn set_ot_shaper_var_u8_auxiliary(&mut self, c: u8) {
28        let v: &mut [u8; 4] = bytemuck::cast_mut(&mut self.var2);
29        v[3] = c;
30    }
31}
32
33pub const MAX_COMBINING_MARKS: usize = 32;
34
35pub type hb_ot_shape_zero_width_marks_type_t = u32;
36pub const HB_OT_SHAPE_ZERO_WIDTH_MARKS_NONE: u32 = 0;
37pub const HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_EARLY: u32 = 1;
38pub const HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_LATE: u32 = 2;
39
40pub type DecomposeFn = fn(&hb_ot_shape_normalize_context_t, char) -> Option<(char, char)>;
41pub type ComposeFn = fn(&hb_ot_shape_normalize_context_t, char, char) -> Option<char>;
42
43pub const DEFAULT_SHAPER: hb_ot_shaper_t = hb_ot_shaper_t {
44    collect_features: None,
45    override_features: None,
46    create_data: None,
47    preprocess_text: None,
48    postprocess_glyphs: None,
49    normalization_preference: HB_OT_SHAPE_NORMALIZATION_MODE_AUTO,
50    decompose: None,
51    compose: None,
52    setup_masks: None,
53    gpos_tag: None,
54    reorder_marks: None,
55    zero_width_marks: HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_LATE,
56    fallback_position: true,
57};
58
59pub struct hb_ot_shaper_t {
60    /// Called during `shape_plan()`.
61    /// Shapers should use plan.map to add their features and callbacks.
62    pub collect_features: Option<fn(&mut hb_ot_shape_planner_t)>,
63
64    /// Called during `shape_plan()`.
65    /// Shapers should use plan.map to override features and add callbacks after
66    /// common features are added.
67    pub override_features: Option<fn(&mut hb_ot_shape_planner_t)>,
68
69    /// Called at the end of `shape_plan()`.
70    /// Whatever shapers return will be accessible through `plan.data()` later.
71    pub create_data: Option<fn(&hb_ot_shape_plan_t) -> Box<dyn Any + Send + Sync>>,
72
73    /// Called during `shape()`.
74    /// Shapers can use to modify text before shaping starts.
75    pub preprocess_text: Option<fn(&hb_ot_shape_plan_t, &hb_font_t, &mut hb_buffer_t)>,
76
77    /// Called during `shape()`.
78    /// Shapers can use to modify text before shaping starts.
79    pub postprocess_glyphs: Option<fn(&hb_ot_shape_plan_t, &hb_font_t, &mut hb_buffer_t)>,
80
81    /// How to normalize.
82    pub normalization_preference: hb_ot_shape_normalization_mode_t,
83
84    /// Called during `shape()`'s normalization.
85    pub decompose: Option<DecomposeFn>,
86
87    /// Called during `shape()`'s normalization.
88    pub compose: Option<ComposeFn>,
89
90    /// Called during `shape()`.
91    /// Shapers should use map to get feature masks and set on buffer.
92    /// Shapers may NOT modify characters.
93    pub setup_masks: Option<fn(&hb_ot_shape_plan_t, &hb_font_t, &mut hb_buffer_t)>,
94
95    /// If not `None`, then must match found GPOS script tag for
96    /// GPOS to be applied.  Otherwise, fallback positioning will be used.
97    pub gpos_tag: Option<hb_tag_t>,
98
99    /// Called during `shape()`.
100    /// Shapers can use to modify ordering of combining marks.
101    pub reorder_marks: Option<fn(&hb_ot_shape_plan_t, &mut hb_buffer_t, usize, usize)>,
102
103    /// If and when to zero-width marks.
104    pub zero_width_marks: hb_ot_shape_zero_width_marks_type_t,
105
106    /// Whether to use fallback mark positioning.
107    pub fallback_position: bool,
108}
109
110// Same as default but no mark advance zeroing / fallback positioning.
111// Dumbest shaper ever, basically.
112pub const DUMBER_SHAPER: hb_ot_shaper_t = hb_ot_shaper_t {
113    collect_features: None,
114    override_features: None,
115    create_data: None,
116    preprocess_text: None,
117    postprocess_glyphs: None,
118    normalization_preference: HB_OT_SHAPE_NORMALIZATION_MODE_AUTO,
119    decompose: None,
120    compose: None,
121    setup_masks: None,
122    gpos_tag: None,
123    reorder_marks: None,
124    zero_width_marks: HB_OT_SHAPE_ZERO_WIDTH_MARKS_NONE,
125    fallback_position: false,
126};
127
128pub fn hb_ot_shape_complex_categorize(
129    script: Script,
130    direction: Direction,
131    gsub_script: Option<hb_tag_t>,
132) -> &'static hb_ot_shaper_t {
133    match script {
134        // Unicode-1.1 additions
135        script::ARABIC
136
137        // Unicode-3.0 additions
138        | script::SYRIAC => {
139            // For Arabic script, use the Arabic shaper even if no OT script tag was found.
140            // This is because we do fallback shaping for Arabic script (and not others).
141            // But note that Arabic shaping is applicable only to horizontal layout; for
142            // vertical text, just use the generic shaper instead.
143            //
144            // TODO: Does this still apply? Arabic fallback shaping was removed.
145            if (gsub_script != Some(hb_tag_t::default_script()) || script == script::ARABIC)
146                && direction.is_horizontal()
147            {
148                &crate::hb::ot_shaper_arabic::ARABIC_SHAPER
149            } else {
150                &DEFAULT_SHAPER
151            }
152        }
153
154        // Unicode-1.1 additions
155        script::THAI
156        | script::LAO => &crate::hb::ot_shaper_thai::THAI_SHAPER,
157
158        // Unicode-1.1 additions
159        script::HANGUL => &crate::hb::ot_shaper_hangul::HANGUL_SHAPER,
160
161        // Unicode-1.1 additions
162        script::HEBREW => &crate::hb::ot_shaper_hebrew::HEBREW_SHAPER,
163
164        // Unicode-1.1 additions
165        script::BENGALI
166        | script::DEVANAGARI
167        | script::GUJARATI
168        | script::GURMUKHI
169        | script::KANNADA
170        | script::MALAYALAM
171        | script::ORIYA
172        | script::TAMIL
173        | script::TELUGU => {
174            // If the designer designed the font for the 'DFLT' script,
175            // (or we ended up arbitrarily pick 'latn'), use the default shaper.
176            // Otherwise, use the specific shaper.
177            //
178            // If it's indy3 tag, send to USE.
179            if gsub_script == Some(hb_tag_t::default_script()) ||
180               gsub_script == Some(hb_tag_t::from_bytes(b"latn")) {
181                &DEFAULT_SHAPER
182            } else if gsub_script.map_or(false, |tag| tag.to_bytes()[3] == b'3') {
183                &crate::hb::ot_shaper_use::UNIVERSAL_SHAPER
184            } else {
185                &crate::hb::ot_shaper_indic::INDIC_SHAPER
186            }
187        }
188
189        script::KHMER => &crate::hb::ot_shaper_khmer::KHMER_SHAPER,
190
191        script::MYANMAR => {
192            // If the designer designed the font for the 'DFLT' script,
193            // (or we ended up arbitrarily pick 'latn'), use the default shaper.
194            // Otherwise, use the specific shaper.
195            //
196            // If designer designed for 'mymr' tag, also send to default
197            // shaper.  That's tag used from before Myanmar shaping spec
198            // was developed.  The shaping spec uses 'mym2' tag.
199            if gsub_script == Some(hb_tag_t::default_script()) ||
200               gsub_script == Some(hb_tag_t::from_bytes(b"latn")) ||
201               gsub_script == Some(hb_tag_t::from_bytes(b"mymr"))
202            {
203                &DEFAULT_SHAPER
204            } else {
205                &crate::hb::ot_shaper_myanmar::MYANMAR_SHAPER
206            }
207        }
208
209        // https://github.com/harfbuzz/harfbuzz/issues/1162
210        script::MYANMAR_ZAWGYI => &crate::hb::ot_shaper_myanmar::MYANMAR_ZAWGYI_SHAPER,
211
212        // Unicode-2.0 additions
213        script::TIBETAN
214
215        // Unicode-3.0 additions
216        | script::MONGOLIAN
217        | script::SINHALA
218
219        // Unicode-3.2 additions
220        | script::BUHID
221        | script::HANUNOO
222        | script::TAGALOG
223        | script::TAGBANWA
224
225        // Unicode-4.0 additions
226        | script::LIMBU
227        | script::TAI_LE
228
229        // Unicode-4.1 additions
230        | script::BUGINESE
231        | script::KHAROSHTHI
232        | script::SYLOTI_NAGRI
233        | script::TIFINAGH
234
235        // Unicode-5.0 additions
236        | script::BALINESE
237        | script::NKO
238        | script::PHAGS_PA
239
240        // Unicode-5.1 additions
241        | script::CHAM
242        | script::KAYAH_LI
243        | script::LEPCHA
244        | script::REJANG
245        | script::SAURASHTRA
246        | script::SUNDANESE
247
248        // Unicode-5.2 additions
249        | script::EGYPTIAN_HIEROGLYPHS
250        | script::JAVANESE
251        | script::KAITHI
252        | script::MEETEI_MAYEK
253        | script::TAI_THAM
254        | script::TAI_VIET
255
256        // Unicode-6.0 additions
257        | script::BATAK
258        | script::BRAHMI
259        | script::MANDAIC
260
261        // Unicode-6.1 additions
262        | script::CHAKMA
263        | script::MIAO
264        | script::SHARADA
265        | script::TAKRI
266
267        // Unicode-7.0 additions
268        | script::DUPLOYAN
269        | script::GRANTHA
270        | script::KHOJKI
271        | script::KHUDAWADI
272        | script::MAHAJANI
273        | script::MANICHAEAN
274        | script::MODI
275        | script::PAHAWH_HMONG
276        | script::PSALTER_PAHLAVI
277        | script::SIDDHAM
278        | script::TIRHUTA
279
280        // Unicode-8.0 additions
281        | script::AHOM
282        | script::MULTANI
283
284        // Unicode-9.0 additions
285        | script::ADLAM
286        | script::BHAIKSUKI
287        | script::MARCHEN
288        | script::NEWA
289
290        // Unicode-10.0 additions
291        | script::MASARAM_GONDI
292        | script::SOYOMBO
293        | script::ZANABAZAR_SQUARE
294
295        // Unicode-11.0 additions
296        | script::DOGRA
297        | script::GUNJALA_GONDI
298        | script::HANIFI_ROHINGYA
299        | script::MAKASAR
300        | script::MEDEFAIDRIN
301        | script::OLD_SOGDIAN
302        | script::SOGDIAN
303
304        // Unicode-12.0 additions
305        | script::ELYMAIC
306        | script::NANDINAGARI
307        | script::NYIAKENG_PUACHUE_HMONG
308        | script::WANCHO
309
310        // Unicode-13.0 additions
311        | script::CHORASMIAN
312        | script::DIVES_AKURU
313        | script::KHITAN_SMALL_SCRIPT
314        | script::YEZIDI
315
316        // Unicode-14.0 additions
317        | script::CYPRO_MINOAN
318        | script::OLD_UYGHUR
319        | script::TANGSA
320        | script::TOTO
321        | script::VITHKUQI
322
323        // Unicode-15.0 additions
324        | script::KAWI
325        | script::NAG_MUNDARI
326
327        // Unicode-16.0 additions
328        | script::GARAY
329        | script::GURUNG_KHEMA
330        | script::KIRAT_RAI
331        | script::OL_ONAL
332        | script::SUNUWAR
333        | script::TODHRI
334        | script::TULU_TIGALARI
335
336        => {
337            // If the designer designed the font for the 'DFLT' script,
338            // (or we ended up arbitrarily pick 'latn'), use the default shaper.
339            // Otherwise, use the specific shaper.
340            // Note that for some simple scripts, there may not be *any*
341            // GSUB/GPOS needed, so there may be no scripts found!
342            if gsub_script == Some(hb_tag_t::default_script()) ||
343               gsub_script == Some(hb_tag_t::from_bytes(b"latn")) {
344                &DEFAULT_SHAPER
345            } else {
346                &crate::hb::ot_shaper_use::UNIVERSAL_SHAPER
347            }
348        }
349
350        _ => &DEFAULT_SHAPER
351    }
352}