1use super::buffer::*;
2use super::font_funcs::FontFuncsDispatch;
3use super::ot_layout::*;
4use super::ot_shape_normalize::HB_OT_SHAPE_NORMALIZATION_MODE_AUTO;
5use super::ot_shape_plan::hb_ot_shape_plan_t;
6use super::ot_shaper::*;
7use super::script;
8use super::unicode::GeneralCategory;
9
10pub const THAI_SHAPER: hb_ot_shaper_t = hb_ot_shaper_t {
11 collect_features: None,
12 override_features: None,
13 create_data: None,
14 preprocess_text: Some(preprocess_text),
15 postprocess_glyphs: None,
16 normalization_preference: HB_OT_SHAPE_NORMALIZATION_MODE_AUTO,
17 decompose: None,
18 compose: None,
19 setup_masks: None,
20 gpos_tag: None,
21 reorder_marks: None,
22 zero_width_marks: HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_LATE,
23 fallback_position: false,
24};
25
26#[derive(Clone, Copy, PartialEq)]
27enum Consonant {
28 NC = 0,
29 AC,
30 RC,
31 DC,
32 NotConsonant,
33}
34
35fn get_consonant_type(u: u32) -> Consonant {
36 match u {
37 0x0E1B | 0x0E1D | 0x0E1F => Consonant::AC,
38 0x0E0D | 0x0E10 => Consonant::RC,
39 0x0E0E | 0x0E0F => Consonant::DC,
40 0x0E01..=0x0E2E => Consonant::NC,
41 _ => Consonant::NotConsonant,
42 }
43}
44
45#[derive(Clone, Copy, PartialEq)]
46enum Mark {
47 AV,
48 BV,
49 T,
50 NotMark,
51}
52
53fn get_mark_type(u: u32) -> Mark {
54 match u {
55 0x0E31 | 0x0E34..=0x0E37 | 0x0E47 | 0x0E4D..=0x0E4E => Mark::AV,
56 0x0E38..=0x0E3A => Mark::BV,
57 0x0E48..=0x0E4C => Mark::T,
58 _ => Mark::NotMark,
59 }
60}
61
62#[derive(Clone, Copy, PartialEq)]
63enum Action {
64 NOP,
65 SD,
67 SL,
69 SDL,
71 RD,
73}
74
75#[derive(Clone, Copy)]
76struct PuaMapping {
77 u: u16,
78 win_pua: u16,
79 mac_pua: u16,
80}
81
82impl PuaMapping {
83 const fn new(u: u16, win_pua: u16, mac_pua: u16) -> Self {
84 PuaMapping {
85 u,
86 win_pua,
87 mac_pua,
88 }
89 }
90}
91
92static SD_MAPPINGS: &[PuaMapping] = &[
93 PuaMapping::new(0x0E48, 0xF70A, 0xF88B), PuaMapping::new(0x0E49, 0xF70B, 0xF88E), PuaMapping::new(0x0E4A, 0xF70C, 0xF891), PuaMapping::new(0x0E4B, 0xF70D, 0xF894), PuaMapping::new(0x0E4C, 0xF70E, 0xF897), PuaMapping::new(0x0E38, 0xF718, 0xF89B), PuaMapping::new(0x0E39, 0xF719, 0xF89C), PuaMapping::new(0x0E3A, 0xF71A, 0xF89D), PuaMapping::new(0x0000, 0x0000, 0x0000),
102];
103
104static SDL_MAPPINGS: &[PuaMapping] = &[
105 PuaMapping::new(0x0E48, 0xF705, 0xF88C), PuaMapping::new(0x0E49, 0xF706, 0xF88F), PuaMapping::new(0x0E4A, 0xF707, 0xF892), PuaMapping::new(0x0E4B, 0xF708, 0xF895), PuaMapping::new(0x0E4C, 0xF709, 0xF898), PuaMapping::new(0x0000, 0x0000, 0x0000),
111];
112
113static SL_MAPPINGS: &[PuaMapping] = &[
114 PuaMapping::new(0x0E48, 0xF713, 0xF88A), PuaMapping::new(0x0E49, 0xF714, 0xF88D), PuaMapping::new(0x0E4A, 0xF715, 0xF890), PuaMapping::new(0x0E4B, 0xF716, 0xF893), PuaMapping::new(0x0E4C, 0xF717, 0xF896), PuaMapping::new(0x0E31, 0xF710, 0xF884), PuaMapping::new(0x0E34, 0xF701, 0xF885), PuaMapping::new(0x0E35, 0xF702, 0xF886), PuaMapping::new(0x0E36, 0xF703, 0xF887), PuaMapping::new(0x0E37, 0xF704, 0xF888), PuaMapping::new(0x0E47, 0xF712, 0xF889), PuaMapping::new(0x0E4D, 0xF711, 0xF899), PuaMapping::new(0x0000, 0x0000, 0x0000),
127];
128
129static RD_MAPPINGS: &[PuaMapping] = &[
130 PuaMapping::new(0x0E0D, 0xF70F, 0xF89A), PuaMapping::new(0x0E10, 0xF700, 0xF89E), PuaMapping::new(0x0000, 0x0000, 0x0000),
133];
134
135fn pua_shape(u: u32, action: Action, face: &mut FontFuncsDispatch) -> u32 {
136 let mappings = match action {
137 Action::NOP => return u,
138 Action::SD => SD_MAPPINGS,
139 Action::SL => SL_MAPPINGS,
140 Action::SDL => SDL_MAPPINGS,
141 Action::RD => RD_MAPPINGS,
142 };
143
144 for m in mappings {
145 if m.u as u32 == u {
146 if face.nominal_glyph(m.win_pua as u32).is_some() {
147 return m.win_pua as u32;
148 }
149
150 if face.nominal_glyph(m.mac_pua as u32).is_some() {
151 return m.mac_pua as u32;
152 }
153
154 break;
155 }
156 }
157
158 u
159}
160
161#[derive(Clone, Copy)]
162enum AboveState {
163 T0, T1, T2, T3, }
169
170static ABOVE_START_STATE: &[AboveState] = &[
171 AboveState::T0, AboveState::T1, AboveState::T0, AboveState::T0, AboveState::T3, ];
177
178#[derive(Clone, Copy)]
179struct AboveStateMachineEdge {
180 action: Action,
181 next_state: AboveState,
182}
183
184impl AboveStateMachineEdge {
185 const fn new(action: Action, next_state: AboveState) -> Self {
186 AboveStateMachineEdge { action, next_state }
187 }
188}
189
190type ASME = AboveStateMachineEdge;
191
192static ABOVE_STATE_MACHINE: &[[ASME; 3]] = &[
193 [
196 ASME::new(Action::NOP, AboveState::T3),
197 ASME::new(Action::NOP, AboveState::T0),
198 ASME::new(Action::SD, AboveState::T3),
199 ],
200 [
202 ASME::new(Action::SL, AboveState::T2),
203 ASME::new(Action::NOP, AboveState::T1),
204 ASME::new(Action::SDL, AboveState::T2),
205 ],
206 [
208 ASME::new(Action::NOP, AboveState::T3),
209 ASME::new(Action::NOP, AboveState::T2),
210 ASME::new(Action::SL, AboveState::T3),
211 ],
212 [
214 ASME::new(Action::NOP, AboveState::T3),
215 ASME::new(Action::NOP, AboveState::T3),
216 ASME::new(Action::NOP, AboveState::T3),
217 ],
218];
219
220#[derive(Clone, Copy)]
221enum BelowState {
222 B0,
224 B1,
226 B2,
228}
229
230static BELOW_START_STATE: &[BelowState] = &[
231 BelowState::B0, BelowState::B0, BelowState::B1, BelowState::B2, BelowState::B2, ];
237
238#[derive(Clone, Copy)]
239struct BelowStateMachineEdge {
240 action: Action,
241 next_state: BelowState,
242}
243
244impl BelowStateMachineEdge {
245 const fn new(action: Action, next_state: BelowState) -> Self {
246 BelowStateMachineEdge { action, next_state }
247 }
248}
249
250type BSME = BelowStateMachineEdge;
251
252static BELOW_STATE_MACHINE: &[[BSME; 3]] = &[
253 [
256 BSME::new(Action::NOP, BelowState::B0),
257 BSME::new(Action::NOP, BelowState::B2),
258 BSME::new(Action::NOP, BelowState::B0),
259 ],
260 [
262 BSME::new(Action::NOP, BelowState::B1),
263 BSME::new(Action::RD, BelowState::B2),
264 BSME::new(Action::NOP, BelowState::B1),
265 ],
266 [
268 BSME::new(Action::NOP, BelowState::B2),
269 BSME::new(Action::SD, BelowState::B2),
270 BSME::new(Action::NOP, BelowState::B2),
271 ],
272];
273
274fn do_pua_shaping(face: &mut FontFuncsDispatch, buffer: &mut hb_buffer_t) {
275 let mut above_state = ABOVE_START_STATE[Consonant::NotConsonant as usize];
276 let mut below_state = BELOW_START_STATE[Consonant::NotConsonant as usize];
277 let mut base = 0;
278
279 for i in 0..buffer.len {
280 let mt = get_mark_type(buffer.info[i].glyph_id);
281
282 if mt == Mark::NotMark {
283 let ct = get_consonant_type(buffer.info[i].glyph_id);
284 above_state = ABOVE_START_STATE[ct as usize];
285 below_state = BELOW_START_STATE[ct as usize];
286 base = i;
287 continue;
288 }
289
290 let above_edge = ABOVE_STATE_MACHINE[above_state as usize][mt as usize];
291 let below_edge = BELOW_STATE_MACHINE[below_state as usize][mt as usize];
292 above_state = above_edge.next_state;
293 below_state = below_edge.next_state;
294
295 let action = if above_edge.action != Action::NOP {
297 above_edge.action
298 } else {
299 below_edge.action
300 };
301
302 buffer.unsafe_to_break(Some(base), Some(i));
303 if action == Action::RD {
304 buffer.info[base].glyph_id = pua_shape(buffer.info[base].glyph_id, action, face);
305 } else {
306 buffer.info[i].glyph_id = pua_shape(buffer.info[i].glyph_id, action, face);
307 }
308 }
309}
310
311fn preprocess_text(
313 plan: &hb_ot_shape_plan_t,
314 face: &mut FontFuncsDispatch,
315 buffer: &mut hb_buffer_t,
316) {
317 #[inline]
363 fn is_sara_am(u: u32) -> bool {
364 (u & !0x0080) == 0x0E33
365 }
366 #[inline]
367 fn nikhahit_from_sara_am(u: u32) -> u32 {
368 u - 0x0E33 + 0x0E4D
369 }
370 #[inline]
371 fn sara_aa_from_sara_am(u: u32) -> u32 {
372 u - 1
373 }
374 #[inline]
375 fn is_above_base_mark(u: u32) -> bool {
376 let u = u & !0x0080;
377 matches!(u, 0x0E34..=0x0E37 | 0x0E47..=0x0E4E | 0x0E31..=0x0E31 | 0x0E3B..=0x0E3B)
378 }
379
380 buffer.clear_output();
381 buffer.idx = 0;
382 while buffer.idx < buffer.len {
383 let u = buffer.cur(0).glyph_id;
384 if !is_sara_am(u) {
385 buffer.next_glyph();
386 continue;
387 }
388
389 buffer.output_glyph(nikhahit_from_sara_am(u));
391 {
392 let out_idx = buffer.out_len - 1;
393 let mut info = buffer.out_info_mut()[out_idx];
394 info.set_continuation(&mut buffer.scratch_flags);
395 }
396 buffer.replace_glyph(sara_aa_from_sara_am(u));
397
398 let end = buffer.out_len;
400
401 buffer.out_info_mut()[end - 2].set_general_category(GeneralCategory::NON_SPACING_MARK);
402
403 let mut start = end - 2;
405 while start > 0 && is_above_base_mark(buffer.out_info()[start - 1].glyph_id) {
406 start -= 1;
407 }
408
409 if start + 2 < end {
410 buffer.merge_out_clusters(start, end);
412 let t = buffer.out_info()[end - 2];
413 for i in 0..(end - start - 2) {
414 buffer.out_info_mut()[i + start + 1] = buffer.out_info()[i + start];
415 }
416 buffer.out_info_mut()[start] = t;
417 }
418
419 if start != 0 {
422 buffer.merge_out_grapheme_clusters(start - 1, end);
423 }
424 }
425
426 buffer.sync();
427
428 if plan.script == Some(script::THAI) && !plan.ot_map.found_script(TableIndex::GSUB) {
430 do_pua_shaping(face, buffer);
431 }
432}