rustybuzz/hb/
ot_shaper_myanmar.rs1use super::buffer::hb_buffer_t;
2use super::ot_map::*;
3use super::ot_shape::*;
4use super::ot_shape_normalize::*;
5use super::ot_shape_plan::hb_ot_shape_plan_t;
6use super::ot_shaper::*;
7use super::ot_shaper_indic::{ot_category_t, ot_position_t};
8use super::{hb_font_t, hb_glyph_info_t, hb_tag_t};
9use crate::hb::ot_shaper_indic::ot_category_t::OT_VPre;
10
11pub const MYANMAR_SHAPER: hb_ot_shaper_t = hb_ot_shaper_t {
12 collect_features: Some(collect_features),
13 override_features: None,
14 create_data: None,
15 preprocess_text: None,
16 postprocess_glyphs: None,
17 normalization_preference: HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS_NO_SHORT_CIRCUIT,
18 decompose: None,
19 compose: None,
20 setup_masks: Some(setup_masks),
21 gpos_tag: None,
22 reorder_marks: None,
23 zero_width_marks: HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_EARLY,
24 fallback_position: false,
25};
26
27pub const MYANMAR_ZAWGYI_SHAPER: hb_ot_shaper_t = hb_ot_shaper_t {
31 collect_features: None,
32 override_features: None,
33 create_data: None,
34 preprocess_text: None,
35 postprocess_glyphs: None,
36 normalization_preference: HB_OT_SHAPE_NORMALIZATION_MODE_NONE,
37 decompose: None,
38 compose: None,
39 setup_masks: None,
40 gpos_tag: None,
41 reorder_marks: None,
42 zero_width_marks: HB_OT_SHAPE_ZERO_WIDTH_MARKS_NONE,
43 fallback_position: false,
44};
45
46const MYANMAR_FEATURES: &[hb_tag_t] = &[
47 hb_tag_t::from_bytes(b"rphf"),
51 hb_tag_t::from_bytes(b"pref"),
52 hb_tag_t::from_bytes(b"blwf"),
53 hb_tag_t::from_bytes(b"pstf"),
54 hb_tag_t::from_bytes(b"pres"),
57 hb_tag_t::from_bytes(b"abvs"),
58 hb_tag_t::from_bytes(b"blws"),
59 hb_tag_t::from_bytes(b"psts"),
60];
61
62impl hb_glyph_info_t {
63 fn set_myanmar_properties(&mut self) {
64 let u = self.glyph_id;
65 let (cat, _) = crate::hb::ot_shaper_indic_table::get_categories(u);
66
67 self.set_myanmar_category(cat);
68 }
69}
70
71fn collect_features(planner: &mut hb_ot_shape_planner_t) {
72 planner.ot_map.add_gsub_pause(Some(setup_syllables));
74
75 planner
76 .ot_map
77 .enable_feature(hb_tag_t::from_bytes(b"locl"), F_PER_SYLLABLE, 1);
78 planner
81 .ot_map
82 .enable_feature(hb_tag_t::from_bytes(b"ccmp"), F_PER_SYLLABLE, 1);
83
84 planner.ot_map.add_gsub_pause(Some(reorder_myanmar));
85
86 for feature in MYANMAR_FEATURES.iter().take(4) {
87 planner
88 .ot_map
89 .enable_feature(*feature, F_MANUAL_ZWJ | F_PER_SYLLABLE, 1);
90 planner.ot_map.add_gsub_pause(None);
91 }
92
93 planner.ot_map.add_gsub_pause(Some(syllabic_clear_var)); for feature in MYANMAR_FEATURES.iter().skip(4) {
96 planner.ot_map.enable_feature(*feature, F_MANUAL_ZWJ, 1);
97 }
98}
99
100fn setup_syllables(_: &hb_ot_shape_plan_t, _: &hb_font_t, buffer: &mut hb_buffer_t) -> bool {
101 super::ot_shaper_myanmar_machine::find_syllables_myanmar(buffer);
102
103 let mut start = 0;
104 let mut end = buffer.next_syllable(0);
105 while start < buffer.len {
106 buffer.unsafe_to_break(Some(start), Some(end));
107 start = end;
108 end = buffer.next_syllable(start);
109 }
110
111 false
112}
113
114fn reorder_myanmar(_: &hb_ot_shape_plan_t, face: &hb_font_t, buffer: &mut hb_buffer_t) -> bool {
115 use super::ot_shaper_myanmar_machine::SyllableType;
116
117 let mut ret = false;
118
119 if super::ot_shaper_syllabic::insert_dotted_circles(
120 face,
121 buffer,
122 SyllableType::BrokenCluster as u8,
123 ot_category_t::OT_DOTTEDCIRCLE,
124 None,
125 None,
126 ) {
127 ret = true;
128 }
129
130 let mut start = 0;
131 let mut end = buffer.next_syllable(0);
132 while start < buffer.len {
133 reorder_syllable_myanmar(start, end, buffer);
134 start = end;
135 end = buffer.next_syllable(start);
136 }
137
138 ret
139}
140
141fn reorder_syllable_myanmar(start: usize, end: usize, buffer: &mut hb_buffer_t) {
142 use super::ot_shaper_myanmar_machine::SyllableType;
143
144 let syllable_type = match buffer.info[start].syllable() & 0x0F {
145 0 => SyllableType::ConsonantSyllable,
146 1 => SyllableType::PunctuationCluster,
147 2 => SyllableType::BrokenCluster,
148 3 => SyllableType::NonMyanmarCluster,
149 _ => unreachable!(),
150 };
151
152 match syllable_type {
153 SyllableType::ConsonantSyllable | SyllableType::BrokenCluster => {
155 initial_reordering_consonant_syllable(start, end, buffer);
156 }
157 SyllableType::PunctuationCluster | SyllableType::NonMyanmarCluster => {}
158 }
159}
160
161fn initial_reordering_consonant_syllable(start: usize, end: usize, buffer: &mut hb_buffer_t) {
164 let mut base = end;
165 let mut has_reph = false;
166
167 {
168 let mut limit = start;
169 if start + 3 <= end
170 && buffer.info[start + 0].myanmar_category() == ot_category_t::OT_Ra
171 && buffer.info[start + 1].myanmar_category() == ot_category_t::OT_As
172 && buffer.info[start + 2].myanmar_category() == ot_category_t::OT_H
173 {
174 limit += 3;
175 base = start;
176 has_reph = true;
177 }
178
179 {
180 if !has_reph {
181 base = limit;
182 }
183
184 for i in limit..end {
185 if buffer.info[i].is_consonant() {
186 base = i;
187 break;
188 }
189 }
190 }
191 }
192
193 {
195 let mut i = start;
196 while i < start + if has_reph { 3 } else { 0 } {
197 buffer.info[i].set_myanmar_position(ot_position_t::POS_AFTER_MAIN);
198 i += 1;
199 }
200
201 while i < base {
202 buffer.info[i].set_myanmar_position(ot_position_t::POS_PRE_C);
203 i += 1;
204 }
205
206 if i < end {
207 buffer.info[i].set_myanmar_position(ot_position_t::POS_BASE_C);
208 i += 1;
209 }
210
211 let mut pos = ot_position_t::POS_AFTER_MAIN;
212 for i in i..end {
215 if buffer.info[i].myanmar_category() == ot_category_t::OT_MR {
217 buffer.info[i].set_myanmar_position(ot_position_t::POS_PRE_C);
218 continue;
219 }
220
221 if buffer.info[i].myanmar_category() == OT_VPre {
223 buffer.info[i].set_myanmar_position(ot_position_t::POS_PRE_M);
224 continue;
225 }
226
227 if buffer.info[i].myanmar_category() == ot_category_t::OT_VS {
228 let t = buffer.info[i - 1].myanmar_position();
229 buffer.info[i].set_myanmar_position(t);
230 continue;
231 }
232
233 if pos == ot_position_t::POS_AFTER_MAIN
234 && buffer.info[i].myanmar_category() == ot_category_t::OT_VBlw
235 {
236 pos = ot_position_t::POS_BELOW_C;
237 buffer.info[i].set_myanmar_position(pos);
238 continue;
239 }
240
241 if pos == ot_position_t::POS_BELOW_C
242 && buffer.info[i].myanmar_category() == ot_category_t::OT_A
243 {
244 buffer.info[i].set_myanmar_position(ot_position_t::POS_BEFORE_SUB);
245 continue;
246 }
247
248 if pos == ot_position_t::POS_BELOW_C
249 && buffer.info[i].myanmar_category() == ot_category_t::OT_VBlw
250 {
251 buffer.info[i].set_myanmar_position(pos);
252 continue;
253 }
254
255 if pos == ot_position_t::POS_BELOW_C
256 && buffer.info[i].myanmar_category() != ot_category_t::OT_A
257 {
258 pos = ot_position_t::POS_AFTER_SUB;
259 buffer.info[i].set_myanmar_position(pos);
260 continue;
261 }
262
263 buffer.info[i].set_myanmar_position(pos);
264 }
265 }
266
267 buffer.sort(start, end, |a, b| {
268 a.myanmar_position().cmp(&b.myanmar_position()) == core::cmp::Ordering::Greater
269 });
270
271 let mut first_left_matra = end;
273 let mut last_left_matra = end;
274
275 for i in start..end {
276 if buffer.info[i].myanmar_position() == ot_position_t::POS_PRE_M {
277 if first_left_matra == end {
278 first_left_matra = i;
279 }
280
281 last_left_matra = i;
282 }
283 }
284
285 if first_left_matra < last_left_matra {
287 buffer.reverse_range(first_left_matra, last_left_matra + 1);
289 let mut i = first_left_matra;
291
292 for j in i..=last_left_matra {
293 if buffer.info[j].myanmar_category() == ot_category_t::OT_VPre {
294 buffer.reverse_range(i, j + 1);
295 i = j + 1;
296 }
297 }
298 }
299}
300
301fn setup_masks(_: &hb_ot_shape_plan_t, _: &hb_font_t, buffer: &mut hb_buffer_t) {
302 for info in buffer.info_slice_mut() {
304 info.set_myanmar_properties();
305 }
306}