1 | use super::indic::{category, position}; |
2 | use super::*; |
3 | use crate::buffer::Buffer; |
4 | use crate::ot::{feature, FeatureFlags}; |
5 | use crate::plan::{ShapePlan, ShapePlanner}; |
6 | use crate::{Face, GlyphInfo, Tag}; |
7 | |
8 | pub const MYANMAR_SHAPER: ComplexShaper = ComplexShaper { |
9 | collect_features: Some(collect_features), |
10 | override_features: None, |
11 | create_data: None, |
12 | preprocess_text: None, |
13 | postprocess_glyphs: None, |
14 | normalization_mode: Some(ShapeNormalizationMode::ComposedDiacriticsNoShortCircuit), |
15 | decompose: None, |
16 | compose: None, |
17 | setup_masks: Some(setup_masks), |
18 | gpos_tag: None, |
19 | reorder_marks: None, |
20 | zero_width_marks: Some(ZeroWidthMarksMode::ByGdefEarly), |
21 | fallback_position: false, |
22 | }; |
23 | |
24 | // Ugly Zawgyi encoding. |
25 | // Disable all auto processing. |
26 | // https://github.com/harfbuzz/harfbuzz/issues/1162 |
27 | pub const MYANMAR_ZAWGYI_SHAPER: ComplexShaper = ComplexShaper { |
28 | collect_features: None, |
29 | override_features: None, |
30 | create_data: None, |
31 | preprocess_text: None, |
32 | postprocess_glyphs: None, |
33 | normalization_mode: None, |
34 | decompose: None, |
35 | compose: None, |
36 | setup_masks: None, |
37 | gpos_tag: None, |
38 | reorder_marks: None, |
39 | zero_width_marks: None, |
40 | fallback_position: false, |
41 | }; |
42 | |
43 | const MYANMAR_FEATURES: &[Tag] = &[ |
44 | // Basic features. |
45 | // These features are applied in order, one at a time, after reordering, |
46 | // constrained to the syllable. |
47 | feature::REPH_FORMS, |
48 | feature::PRE_BASE_FORMS, |
49 | feature::BELOW_BASE_FORMS, |
50 | feature::POST_BASE_FORMS, |
51 | // Other features. |
52 | // These features are applied all at once after clearing syllables. |
53 | feature::PRE_BASE_SUBSTITUTIONS, |
54 | feature::ABOVE_BASE_SUBSTITUTIONS, |
55 | feature::BELOW_BASE_SUBSTITUTIONS, |
56 | feature::POST_BASE_SUBSTITUTIONS, |
57 | ]; |
58 | |
59 | impl GlyphInfo { |
60 | fn set_myanmar_properties(&mut self) { |
61 | let u = self.glyph_id; |
62 | let (mut cat, mut pos) = super::indic::get_category_and_position(u); |
63 | |
64 | // Myanmar |
65 | // https://docs.microsoft.com/en-us/typography/script-development/myanmar#analyze |
66 | |
67 | if (0xFE00..=0xFE0F).contains(&u) { |
68 | cat = category::VS; |
69 | } |
70 | |
71 | match u { |
72 | // The spec says C, IndicSyllableCategory doesn't have. |
73 | 0x104E => cat = category::C, |
74 | |
75 | 0x002D | 0x00A0 | 0x00D7 | 0x2012 | 0x2013 | 0x2014 | 0x2015 | 0x2022 | 0x25CC |
76 | | 0x25FB | 0x25FC | 0x25FD | 0x25FE => cat = category::PLACEHOLDER, |
77 | |
78 | 0x1004 | 0x101B | 0x105A => cat = category::RA, |
79 | |
80 | 0x1032 | 0x1036 => cat = category::A, |
81 | |
82 | 0x1039 => cat = category::H, |
83 | |
84 | 0x103A => cat = category::SYMBOL, |
85 | |
86 | 0x1041 | 0x1042 | 0x1043 | 0x1044 | 0x1045 | 0x1046 | 0x1047 | 0x1048 | 0x1049 |
87 | | 0x1090 | 0x1091 | 0x1092 | 0x1093 | 0x1094 | 0x1095 | 0x1096 | 0x1097 | 0x1098 |
88 | | 0x1099 => cat = category::D, |
89 | |
90 | // XXX The spec says D0, but Uniscribe doesn't seem to do. |
91 | 0x1040 => cat = category::D, |
92 | |
93 | 0x103E | 0x1060 => cat = category::X_GROUP, |
94 | |
95 | 0x103C => cat = category::Y_GROUP, |
96 | |
97 | 0x103D | 0x1082 => cat = category::MW, |
98 | |
99 | 0x103B | 0x105E | 0x105F => cat = category::MY, |
100 | |
101 | 0x1063 | 0x1064 | 0x1069 | 0x106A | 0x106B | 0x106C | 0x106D | 0xAA7B => { |
102 | cat = category::PT |
103 | } |
104 | |
105 | 0x1038 | 0x1087 | 0x1088 | 0x1089 | 0x108A | 0x108B | 0x108C | 0x108D | 0x108F |
106 | | 0x109A | 0x109B | 0x109C => cat = category::SM, |
107 | |
108 | 0x104A | 0x104B => cat = category::P, |
109 | |
110 | // https://github.com/harfbuzz/harfbuzz/issues/218 |
111 | 0xAA74 | 0xAA75 | 0xAA76 => cat = category::C, |
112 | |
113 | _ => {} |
114 | } |
115 | |
116 | // Re-assign position. |
117 | |
118 | if cat == category::M { |
119 | match pos { |
120 | position::PRE_C => { |
121 | cat = category::V_PRE; |
122 | pos = position::PRE_M; |
123 | } |
124 | position::BELOW_C => cat = category::V_BLW, |
125 | position::ABOVE_C => cat = category::V_AVB, |
126 | position::POST_C => cat = category::V_PST, |
127 | _ => {} |
128 | } |
129 | } |
130 | |
131 | self.set_indic_category(cat); |
132 | self.set_indic_position(pos); |
133 | } |
134 | } |
135 | |
136 | fn collect_features(planner: &mut ShapePlanner) { |
137 | // Do this before any lookups have been applied. |
138 | planner.ot_map.add_gsub_pause(Some(setup_syllables)); |
139 | |
140 | planner |
141 | .ot_map |
142 | .enable_feature(feature::LOCALIZED_FORMS, FeatureFlags::empty(), 1); |
143 | // The Indic specs do not require ccmp, but we apply it here since if |
144 | // there is a use of it, it's typically at the beginning. |
145 | planner.ot_map.enable_feature( |
146 | feature::GLYPH_COMPOSITION_DECOMPOSITION, |
147 | FeatureFlags::empty(), |
148 | 1, |
149 | ); |
150 | |
151 | planner.ot_map.add_gsub_pause(Some(reorder)); |
152 | |
153 | for feature in MYANMAR_FEATURES.iter().take(4) { |
154 | planner |
155 | .ot_map |
156 | .enable_feature(*feature, FeatureFlags::MANUAL_ZWJ, 1); |
157 | planner.ot_map.add_gsub_pause(None); |
158 | } |
159 | |
160 | planner |
161 | .ot_map |
162 | .add_gsub_pause(Some(crate::ot::clear_syllables)); |
163 | |
164 | for feature in MYANMAR_FEATURES.iter().skip(4) { |
165 | planner |
166 | .ot_map |
167 | .enable_feature(*feature, FeatureFlags::MANUAL_ZWJ, 1); |
168 | } |
169 | } |
170 | |
171 | fn setup_syllables(_: &ShapePlan, _: &Face, buffer: &mut Buffer) { |
172 | super::myanmar_machine::find_syllables_myanmar(buffer); |
173 | |
174 | let mut start: usize = 0; |
175 | let mut end: usize = buffer.next_syllable(start:0); |
176 | while start < buffer.len { |
177 | buffer.unsafe_to_break(start, end); |
178 | start = end; |
179 | end = buffer.next_syllable(start); |
180 | } |
181 | } |
182 | |
183 | fn reorder(_: &ShapePlan, face: &Face, buffer: &mut Buffer) { |
184 | use super::myanmar_machine::SyllableType; |
185 | |
186 | syllabic::insert_dotted_circles( |
187 | face, |
188 | buffer, |
189 | broken_syllable_type:SyllableType::BrokenCluster as u8, |
190 | dottedcircle_category:category::PLACEHOLDER, |
191 | repha_category:None, |
192 | dottedcircle_position:None, |
193 | ); |
194 | |
195 | let mut start: usize = 0; |
196 | let mut end: usize = buffer.next_syllable(start:0); |
197 | while start < buffer.len { |
198 | reorder_syllable(start, end, buffer); |
199 | start = end; |
200 | end = buffer.next_syllable(start); |
201 | } |
202 | } |
203 | |
204 | fn reorder_syllable(start: usize, end: usize, buffer: &mut Buffer) { |
205 | use super::myanmar_machine::SyllableType; |
206 | |
207 | let syllable_type: SyllableType = match buffer.info[start].syllable() & 0x0F { |
208 | 0 => SyllableType::ConsonantSyllable, |
209 | 1 => SyllableType::PunctuationCluster, |
210 | 2 => SyllableType::BrokenCluster, |
211 | 3 => SyllableType::NonMyanmarCluster, |
212 | _ => unreachable!(), |
213 | }; |
214 | |
215 | match syllable_type { |
216 | // We already inserted dotted-circles, so just call the consonant_syllable. |
217 | SyllableType::ConsonantSyllable | SyllableType::BrokenCluster => { |
218 | initial_reordering_consonant_syllable(start, end, buffer); |
219 | } |
220 | SyllableType::PunctuationCluster | SyllableType::NonMyanmarCluster => {} |
221 | } |
222 | } |
223 | |
224 | // Rules from: |
225 | // https://docs.microsoft.com/en-us/typography/script-development/myanmar |
226 | fn initial_reordering_consonant_syllable(start: usize, end: usize, buffer: &mut Buffer) { |
227 | let mut base = end; |
228 | let mut has_reph = false; |
229 | |
230 | { |
231 | let mut limit = start; |
232 | if start + 3 <= end |
233 | && buffer.info[start + 0].indic_category() == category::RA |
234 | && buffer.info[start + 1].indic_category() == category::SYMBOL |
235 | && buffer.info[start + 2].indic_category() == category::H |
236 | { |
237 | limit += 3; |
238 | base = start; |
239 | has_reph = true; |
240 | } |
241 | |
242 | { |
243 | if !has_reph { |
244 | base = limit; |
245 | } |
246 | |
247 | for i in limit..end { |
248 | if buffer.info[i].is_consonant() { |
249 | base = i; |
250 | break; |
251 | } |
252 | } |
253 | } |
254 | } |
255 | |
256 | // Reorder! |
257 | { |
258 | let mut i = start; |
259 | while i < start + if has_reph { 3 } else { 0 } { |
260 | buffer.info[i].set_indic_position(position::AFTER_MAIN); |
261 | i += 1; |
262 | } |
263 | |
264 | while i < base { |
265 | buffer.info[i].set_indic_position(position::PRE_C); |
266 | i += 1; |
267 | } |
268 | |
269 | if i < end { |
270 | buffer.info[i].set_indic_position(position::BASE_C); |
271 | i += 1; |
272 | } |
273 | |
274 | let mut pos = position::AFTER_MAIN; |
275 | // The following loop may be ugly, but it implements all of |
276 | // Myanmar reordering! |
277 | for i in i..end { |
278 | // Pre-base reordering |
279 | if buffer.info[i].indic_category() == category::Y_GROUP { |
280 | buffer.info[i].set_indic_position(position::PRE_C); |
281 | continue; |
282 | } |
283 | |
284 | // Left matra |
285 | if buffer.info[i].indic_position() < position::BASE_C { |
286 | continue; |
287 | } |
288 | |
289 | if buffer.info[i].indic_category() == category::VS { |
290 | let t = buffer.info[i - 1].indic_position(); |
291 | buffer.info[i].set_indic_position(t); |
292 | continue; |
293 | } |
294 | |
295 | if pos == position::AFTER_MAIN && buffer.info[i].indic_category() == category::V_BLW { |
296 | pos = position::BELOW_C; |
297 | buffer.info[i].set_indic_position(pos); |
298 | continue; |
299 | } |
300 | |
301 | if pos == position::BELOW_C && buffer.info[i].indic_category() == category::A { |
302 | buffer.info[i].set_indic_position(position::BEFORE_SUB); |
303 | continue; |
304 | } |
305 | |
306 | if pos == position::BELOW_C && buffer.info[i].indic_category() == category::V_BLW { |
307 | buffer.info[i].set_indic_position(pos); |
308 | continue; |
309 | } |
310 | |
311 | if pos == position::BELOW_C && buffer.info[i].indic_category() != category::A { |
312 | pos = position::AFTER_SUB; |
313 | buffer.info[i].set_indic_position(pos); |
314 | continue; |
315 | } |
316 | |
317 | buffer.info[i].set_indic_position(pos); |
318 | } |
319 | } |
320 | |
321 | buffer.sort(start, end, |a, b| { |
322 | a.indic_position().cmp(&b.indic_position()) == core::cmp::Ordering::Greater |
323 | }); |
324 | } |
325 | |
326 | fn setup_masks(_: &ShapePlan, _: &Face, buffer: &mut Buffer) { |
327 | // We cannot setup masks here. We save information about characters |
328 | // and setup masks later on in a pause-callback. |
329 | for info: &mut GlyphInfo in buffer.info_slice_mut() { |
330 | info.set_myanmar_properties(); |
331 | } |
332 | } |
333 | |