1use super::buffer::*;
2use super::ot_layout::*;
3use super::ot_shape_normalize::HB_OT_SHAPE_NORMALIZATION_MODE_AUTO;
4use super::ot_shape_plan::hb_ot_shape_plan_t;
5use super::ot_shaper::*;
6use super::unicode::hb_unicode_general_category_t;
7use super::{hb_font_t, script};
8
9pub const THAI_SHAPER: hb_ot_shaper_t = hb_ot_shaper_t {
10 collect_features: None,
11 override_features: None,
12 create_data: None,
13 preprocess_text: Some(preprocess_text),
14 postprocess_glyphs: None,
15 normalization_preference: HB_OT_SHAPE_NORMALIZATION_MODE_AUTO,
16 decompose: None,
17 compose: None,
18 setup_masks: None,
19 gpos_tag: None,
20 reorder_marks: None,
21 zero_width_marks: HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_LATE,
22 fallback_position: false,
23};
24
25#[derive(Clone, Copy, PartialEq)]
26enum Consonant {
27 NC = 0,
28 AC,
29 RC,
30 DC,
31 NotConsonant,
32}
33
34fn get_consonant_type(u: u32) -> Consonant {
35 match u {
36 0x0E1B | 0x0E1D | 0x0E1F => Consonant::AC,
37 0x0E0D | 0x0E10 => Consonant::RC,
38 0x0E0E | 0x0E0F => Consonant::DC,
39 0x0E01..=0x0E2E => Consonant::NC,
40 _ => Consonant::NotConsonant,
41 }
42}
43
44#[derive(Clone, Copy, PartialEq)]
45enum Mark {
46 AV,
47 BV,
48 T,
49 NotMark,
50}
51
52fn get_mark_type(u: u32) -> Mark {
53 match u {
54 0x0E31 | 0x0E34..=0x0E37 | 0x0E47 | 0x0E4D..=0x0E4E => Mark::AV,
55 0x0E38..=0x0E3A => Mark::BV,
56 0x0E48..=0x0E4C => Mark::T,
57 _ => Mark::NotMark,
58 }
59}
60
61#[derive(Clone, Copy, PartialEq)]
62enum Action {
63 NOP,
64 SD,
66 SL,
68 SDL,
70 RD,
72}
73
74#[derive(Clone, Copy)]
75struct PuaMapping {
76 u: u16,
77 win_pua: u16,
78 mac_pua: u16,
79}
80
81impl PuaMapping {
82 const fn new(u: u16, win_pua: u16, mac_pua: u16) -> Self {
83 PuaMapping {
84 u,
85 win_pua,
86 mac_pua,
87 }
88 }
89}
90
91const SD_MAPPINGS: &[PuaMapping] = &[
92 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),
101];
102
103const SDL_MAPPINGS: &[PuaMapping] = &[
104 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),
110];
111
112const SL_MAPPINGS: &[PuaMapping] = &[
113 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),
126];
127
128const RD_MAPPINGS: &[PuaMapping] = &[
129 PuaMapping::new(0x0E0D, 0xF70F, 0xF89A), PuaMapping::new(0x0E10, 0xF700, 0xF89E), PuaMapping::new(0x0000, 0x0000, 0x0000),
132];
133
134fn pua_shape(u: u32, action: Action, face: &hb_font_t) -> u32 {
135 let mappings = match action {
136 Action::NOP => return u,
137 Action::SD => SD_MAPPINGS,
138 Action::SL => SL_MAPPINGS,
139 Action::SDL => SDL_MAPPINGS,
140 Action::RD => RD_MAPPINGS,
141 };
142
143 for m in mappings {
144 if m.u as u32 == u {
145 if face.get_nominal_glyph(m.win_pua as u32).is_some() {
146 return m.win_pua as u32;
147 }
148
149 if face.get_nominal_glyph(m.mac_pua as u32).is_some() {
150 return m.mac_pua as u32;
151 }
152
153 break;
154 }
155 }
156
157 u
158}
159
160#[derive(Clone, Copy)]
161enum AboveState {
162 T0, T1, T2, T3, }
168
169const ABOVE_START_STATE: &[AboveState] = &[
170 AboveState::T0, AboveState::T1, AboveState::T0, AboveState::T0, AboveState::T3, ];
176
177#[derive(Clone, Copy)]
178struct AboveStateMachineEdge {
179 action: Action,
180 next_state: AboveState,
181}
182
183impl AboveStateMachineEdge {
184 const fn new(action: Action, next_state: AboveState) -> Self {
185 AboveStateMachineEdge { action, next_state }
186 }
187}
188
189type ASME = AboveStateMachineEdge;
190
191const ABOVE_STATE_MACHINE: &[[ASME; 3]] = &[
192 [
195 ASME::new(Action::NOP, AboveState::T3),
196 ASME::new(Action::NOP, AboveState::T0),
197 ASME::new(Action::SD, AboveState::T3),
198 ],
199 [
201 ASME::new(Action::SL, AboveState::T2),
202 ASME::new(Action::NOP, AboveState::T1),
203 ASME::new(Action::SDL, AboveState::T2),
204 ],
205 [
207 ASME::new(Action::NOP, AboveState::T3),
208 ASME::new(Action::NOP, AboveState::T2),
209 ASME::new(Action::SL, AboveState::T3),
210 ],
211 [
213 ASME::new(Action::NOP, AboveState::T3),
214 ASME::new(Action::NOP, AboveState::T3),
215 ASME::new(Action::NOP, AboveState::T3),
216 ],
217];
218
219#[derive(Clone, Copy)]
220enum BelowState {
221 B0,
223 B1,
225 B2,
227}
228
229const BELOW_START_STATE: &[BelowState] = &[
230 BelowState::B0, BelowState::B0, BelowState::B1, BelowState::B2, BelowState::B2, ];
236
237#[derive(Clone, Copy)]
238struct BelowStateMachineEdge {
239 action: Action,
240 next_state: BelowState,
241}
242
243impl BelowStateMachineEdge {
244 const fn new(action: Action, next_state: BelowState) -> Self {
245 BelowStateMachineEdge { action, next_state }
246 }
247}
248
249type BSME = BelowStateMachineEdge;
250
251const BELOW_STATE_MACHINE: &[[BSME; 3]] = &[
252 [
255 BSME::new(Action::NOP, BelowState::B0),
256 BSME::new(Action::NOP, BelowState::B2),
257 BSME::new(Action::NOP, BelowState::B0),
258 ],
259 [
261 BSME::new(Action::NOP, BelowState::B1),
262 BSME::new(Action::RD, BelowState::B2),
263 BSME::new(Action::NOP, BelowState::B1),
264 ],
265 [
267 BSME::new(Action::NOP, BelowState::B2),
268 BSME::new(Action::SD, BelowState::B2),
269 BSME::new(Action::NOP, BelowState::B2),
270 ],
271];
272
273fn do_pua_shaping(face: &hb_font_t, buffer: &mut hb_buffer_t) {
274 let mut above_state = ABOVE_START_STATE[Consonant::NotConsonant as usize];
275 let mut below_state = BELOW_START_STATE[Consonant::NotConsonant as usize];
276 let mut base = 0;
277
278 for i in 0..buffer.len {
279 let mt = get_mark_type(buffer.info[i].glyph_id);
280
281 if mt == Mark::NotMark {
282 let ct = get_consonant_type(buffer.info[i].glyph_id);
283 above_state = ABOVE_START_STATE[ct as usize];
284 below_state = BELOW_START_STATE[ct as usize];
285 base = i;
286 continue;
287 }
288
289 let above_edge = ABOVE_STATE_MACHINE[above_state as usize][mt as usize];
290 let below_edge = BELOW_STATE_MACHINE[below_state as usize][mt as usize];
291 above_state = above_edge.next_state;
292 below_state = below_edge.next_state;
293
294 let action = if above_edge.action != Action::NOP {
296 above_edge.action
297 } else {
298 below_edge.action
299 };
300
301 buffer.unsafe_to_break(Some(base), Some(i));
302 if action == Action::RD {
303 buffer.info[base].glyph_id = pua_shape(buffer.info[base].glyph_id, action, face);
304 } else {
305 buffer.info[i].glyph_id = pua_shape(buffer.info[i].glyph_id, action, face);
306 }
307 }
308}
309
310fn preprocess_text(plan: &hb_ot_shape_plan_t, face: &hb_font_t, buffer: &mut hb_buffer_t) {
312 #[inline]
358 fn is_sara_am(u: u32) -> bool {
359 (u & !0x0080) == 0x0E33
360 }
361 #[inline]
362 fn nikhahit_from_sara_am(u: u32) -> u32 {
363 u - 0x0E33 + 0x0E4D
364 }
365 #[inline]
366 fn sara_aa_from_sara_am(u: u32) -> u32 {
367 u - 1
368 }
369 #[inline]
370 fn is_above_base_mark(u: u32) -> bool {
371 let u = u & !0x0080;
372 matches!(u, 0x0E34..=0x0E37 | 0x0E47..=0x0E4E | 0x0E31..=0x0E31 | 0x0E3B..=0x0E3B)
373 }
374
375 buffer.clear_output();
376 buffer.idx = 0;
377 while buffer.idx < buffer.len {
378 let u = buffer.cur(0).glyph_id;
379 if !is_sara_am(u) {
380 buffer.next_glyph();
381 continue;
382 }
383
384 buffer.output_glyph(nikhahit_from_sara_am(u));
386 {
387 let out_idx = buffer.out_len - 1;
388 _hb_glyph_info_set_continuation(&mut buffer.out_info_mut()[out_idx]);
389 }
390 buffer.replace_glyph(sara_aa_from_sara_am(u));
391
392 let end = buffer.out_len;
394 _hb_glyph_info_set_general_category(
395 &mut buffer.out_info_mut()[end - 2],
396 hb_unicode_general_category_t::NonspacingMark,
397 );
398
399 let mut start = end - 2;
401 while start > 0 && is_above_base_mark(buffer.out_info()[start - 1].glyph_id) {
402 start -= 1;
403 }
404
405 if start + 2 < end {
406 buffer.merge_out_clusters(start, end);
408 let t = buffer.out_info()[end - 2];
409 for i in 0..(end - start - 2) {
410 buffer.out_info_mut()[i + start + 1] = buffer.out_info()[i + start];
411 }
412 buffer.out_info_mut()[start] = t;
413 } else {
414 if start != 0 && buffer.cluster_level == HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES {
417 buffer.merge_out_clusters(start - 1, end);
418 }
419 }
420 }
421
422 buffer.sync();
423
424 if plan.script == Some(script::THAI) && !plan.ot_map.found_script(TableIndex::GSUB) {
426 do_pua_shaping(face, buffer);
427 }
428}