rustybuzz/hb/
ot_shape_complex.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 complex_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_complex_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 complex_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_complex_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 const DEFAULT_SHAPER: hb_ot_complex_shaper_t = hb_ot_complex_shaper_t {
41    collect_features: None,
42    override_features: None,
43    create_data: None,
44    preprocess_text: None,
45    postprocess_glyphs: None,
46    normalization_preference: HB_OT_SHAPE_NORMALIZATION_MODE_AUTO,
47    decompose: None,
48    compose: None,
49    setup_masks: None,
50    gpos_tag: None,
51    reorder_marks: None,
52    zero_width_marks: HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_LATE,
53    fallback_position: true,
54};
55
56pub struct hb_ot_complex_shaper_t {
57    /// Called during `shape_plan()`.
58    /// Shapers should use plan.map to add their features and callbacks.
59    pub collect_features: Option<fn(&mut hb_ot_shape_planner_t)>,
60
61    /// Called during `shape_plan()`.
62    /// Shapers should use plan.map to override features and add callbacks after
63    /// common features are added.
64    pub override_features: Option<fn(&mut hb_ot_shape_planner_t)>,
65
66    /// Called at the end of `shape_plan()`.
67    /// Whatever shapers return will be accessible through `plan.data()` later.
68    pub create_data: Option<fn(&hb_ot_shape_plan_t) -> Box<dyn Any + Send + Sync>>,
69
70    /// Called during `shape()`.
71    /// Shapers can use to modify text before shaping starts.
72    pub preprocess_text: Option<fn(&hb_ot_shape_plan_t, &hb_font_t, &mut hb_buffer_t)>,
73
74    /// Called during `shape()`.
75    /// Shapers can use to modify text before shaping starts.
76    pub postprocess_glyphs: Option<fn(&hb_ot_shape_plan_t, &hb_font_t, &mut hb_buffer_t)>,
77
78    /// How to normalize.
79    pub normalization_preference: hb_ot_shape_normalization_mode_t,
80
81    /// Called during `shape()`'s normalization.
82    pub decompose: Option<fn(&hb_ot_shape_normalize_context_t, char) -> Option<(char, char)>>,
83
84    /// Called during `shape()`'s normalization.
85    pub compose: Option<fn(&hb_ot_shape_normalize_context_t, char, char) -> Option<char>>,
86
87    /// Called during `shape()`.
88    /// Shapers should use map to get feature masks and set on buffer.
89    /// Shapers may NOT modify characters.
90    pub setup_masks: Option<fn(&hb_ot_shape_plan_t, &hb_font_t, &mut hb_buffer_t)>,
91
92    /// If not `None`, then must match found GPOS script tag for
93    /// GPOS to be applied.  Otherwise, fallback positioning will be used.
94    pub gpos_tag: Option<hb_tag_t>,
95
96    /// Called during `shape()`.
97    /// Shapers can use to modify ordering of combining marks.
98    pub reorder_marks: Option<fn(&hb_ot_shape_plan_t, &mut hb_buffer_t, usize, usize)>,
99
100    /// If and when to zero-width marks.
101    pub zero_width_marks: hb_ot_shape_zero_width_marks_type_t,
102
103    /// Whether to use fallback mark positioning.
104    pub fallback_position: bool,
105}
106
107// Same as default but no mark advance zeroing / fallback positioning.
108// Dumbest shaper ever, basically.
109pub const DUMBER_SHAPER: hb_ot_complex_shaper_t = hb_ot_complex_shaper_t {
110    collect_features: None,
111    override_features: None,
112    create_data: None,
113    preprocess_text: None,
114    postprocess_glyphs: None,
115    normalization_preference: HB_OT_SHAPE_NORMALIZATION_MODE_AUTO,
116    decompose: None,
117    compose: None,
118    setup_masks: None,
119    gpos_tag: None,
120    reorder_marks: None,
121    zero_width_marks: HB_OT_SHAPE_ZERO_WIDTH_MARKS_NONE,
122    fallback_position: false,
123};
124
125pub fn hb_ot_shape_complex_categorize(
126    script: Script,
127    direction: Direction,
128    chosen_gsub_script: Option<hb_tag_t>,
129) -> &'static hb_ot_complex_shaper_t {
130    match script {
131        // Unicode-1.1 additions
132        script::ARABIC
133
134        // Unicode-3.0 additions
135        | script::SYRIAC => {
136            // For Arabic script, use the Arabic shaper even if no OT script tag was found.
137            // This is because we do fallback shaping for Arabic script (and not others).
138            // But note that Arabic shaping is applicable only to horizontal layout; for
139            // vertical text, just use the generic shaper instead.
140            //
141            // TODO: Does this still apply? Arabic fallback shaping was removed.
142            if (chosen_gsub_script != Some(hb_tag_t::default_script()) || script == script::ARABIC)
143                && direction.is_horizontal()
144            {
145                &crate::hb::ot_shape_complex_arabic::ARABIC_SHAPER
146            } else {
147                &DEFAULT_SHAPER
148            }
149        }
150
151        // Unicode-1.1 additions
152        script::THAI
153        | script::LAO => &crate::hb::ot_shape_complex_thai::THAI_SHAPER,
154
155        // Unicode-1.1 additions
156        script::HANGUL => &crate::hb::ot_shape_complex_hangul::HANGUL_SHAPER,
157
158        // Unicode-1.1 additions
159        script::HEBREW => &crate::hb::ot_shape_complex_hebrew::HEBREW_SHAPER,
160
161        // Unicode-1.1 additions
162        script::BENGALI
163        | script::DEVANAGARI
164        | script::GUJARATI
165        | script::GURMUKHI
166        | script::KANNADA
167        | script::MALAYALAM
168        | script::ORIYA
169        | script::TAMIL
170        | script::TELUGU
171
172        // Unicode-3.0 additions
173        | script::SINHALA => {
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 chosen_gsub_script == Some(hb_tag_t::default_script()) ||
180               chosen_gsub_script == Some(hb_tag_t::from_bytes(b"latn")) {
181                &DEFAULT_SHAPER
182            } else if chosen_gsub_script.map_or(false, |tag| tag.to_bytes()[3] == b'3') {
183                &crate::hb::ot_shape_complex_use::UNIVERSAL_SHAPER
184            } else {
185                &crate::hb::ot_shape_complex_indic::INDIC_SHAPER
186            }
187        }
188
189        script::KHMER => &crate::hb::ot_shape_complex_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 chosen_gsub_script == Some(hb_tag_t::default_script()) ||
200               chosen_gsub_script == Some(hb_tag_t::from_bytes(b"latn")) ||
201               chosen_gsub_script == Some(hb_tag_t::from_bytes(b"mymr"))
202            {
203                &DEFAULT_SHAPER
204            } else {
205                &crate::hb::ot_shape_complex_myanmar::MYANMAR_SHAPER
206            }
207        }
208
209        // https://github.com/harfbuzz/harfbuzz/issues/1162
210        script::MYANMAR_ZAWGYI => &crate::hb::ot_shape_complex_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            // If the designer designed the font for the 'DFLT' script,
323            // (or we ended up arbitrarily pick 'latn'), use the default shaper.
324            // Otherwise, use the specific shaper.
325            // Note that for some simple scripts, there may not be *any*
326            // GSUB/GPOS needed, so there may be no scripts found!
327            if chosen_gsub_script == Some(hb_tag_t::default_script()) ||
328               chosen_gsub_script == Some(hb_tag_t::from_bytes(b"latn")) {
329                &DEFAULT_SHAPER
330            } else {
331                &crate::hb::ot_shape_complex_use::UNIVERSAL_SHAPER
332            }
333        }
334
335        _ => &DEFAULT_SHAPER
336    }
337}