1 | use super::buffer::hb_buffer_t; |
2 | use super::ot_map::*; |
3 | use super::ot_shape::*; |
4 | use super::ot_shape_normalize::*; |
5 | use super::ot_shape_plan::hb_ot_shape_plan_t; |
6 | use super::ot_shaper::*; |
7 | use super::ot_shaper_indic::{ot_category_t, ot_position_t}; |
8 | use super::{hb_font_t, hb_glyph_info_t, hb_tag_t}; |
9 | use crate::hb::ot_shaper_indic::ot_category_t::OT_VPre; |
10 | |
11 | pub 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 | |
27 | // Ugly Zawgyi encoding. |
28 | // Disable all auto processing. |
29 | // https://github.com/harfbuzz/harfbuzz/issues/1162 |
30 | pub 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 | |
46 | const MYANMAR_FEATURES: &[hb_tag_t] = &[ |
47 | // Basic features. |
48 | // These features are applied in order, one at a time, after reordering, |
49 | // constrained to the syllable. |
50 | 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 | // Other features. |
55 | // These features are applied all at once after clearing syllables. |
56 | 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 | |
62 | impl hb_glyph_info_t { |
63 | fn set_myanmar_properties(&mut self) { |
64 | let u: u32 = self.glyph_id; |
65 | let (cat: u8, _) = crate::hb::ot_shaper_indic_table::get_categories(u); |
66 | |
67 | self.set_myanmar_category(cat); |
68 | } |
69 | } |
70 | |
71 | fn collect_features(planner: &mut hb_ot_shape_planner_t) { |
72 | // Do this before any lookups have been applied. |
73 | 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 | // The Indic specs do not require ccmp, but we apply it here since if |
79 | // there is a use of it, it's typically at the beginning. |
80 | 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)); // Don't need syllables anymore. |
94 | |
95 | for feature in MYANMAR_FEATURES.iter().skip(4) { |
96 | planner.ot_map.enable_feature(*feature, F_MANUAL_ZWJ, 1); |
97 | } |
98 | } |
99 | |
100 | fn 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: usize = 0; |
104 | let mut end: usize = buffer.next_syllable(start:0); |
105 | while start < buffer.len { |
106 | buffer.unsafe_to_break(start:Some(start), end:Some(end)); |
107 | start = end; |
108 | end = buffer.next_syllable(start); |
109 | } |
110 | |
111 | false |
112 | } |
113 | |
114 | fn 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 | |
141 | fn 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: SyllableType = 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 | // We already inserted dotted-circles, so just call the consonant_syllable. |
154 | SyllableType::ConsonantSyllable | SyllableType::BrokenCluster => { |
155 | initial_reordering_consonant_syllable(start, end, buffer); |
156 | } |
157 | SyllableType::PunctuationCluster | SyllableType::NonMyanmarCluster => {} |
158 | } |
159 | } |
160 | |
161 | // Rules from: |
162 | // https://docs.microsoft.com/en-us/typography/script-development/myanmar |
163 | fn 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 | // Reorder! |
194 | { |
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 | // The following loop may be ugly, but it implements all of |
213 | // Myanmar reordering! |
214 | for i in i..end { |
215 | // Pre-base reordering |
216 | 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 | // Left matra |
222 | 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 | // Flip left-mantra sequence |
272 | 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 | // https://github.com/harfbuzz/harfbuzz/issues/3863 |
286 | if first_left_matra < last_left_matra { |
287 | // No need to merge clusters, done already? |
288 | buffer.reverse_range(first_left_matra, last_left_matra + 1); |
289 | // Reverse back VS, etc. |
290 | 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 | |
301 | fn setup_masks(_: &hb_ot_shape_plan_t, _: &hb_font_t, buffer: &mut hb_buffer_t) { |
302 | // No masks, we just save information about characters. |
303 | for info: &mut hb_glyph_info_t in buffer.info_slice_mut() { |
304 | info.set_myanmar_properties(); |
305 | } |
306 | } |
307 | |