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}
353