Imported Upstream version 8.2.2
[platform/upstream/harfbuzz.git] / src / hb-ot-shaper-myanmar.cc
1 /*
2  * Copyright © 2011,2012,2013  Google, Inc.
3  *
4  *  This is part of HarfBuzz, a text shaping library.
5  *
6  * Permission is hereby granted, without written agreement and without
7  * license or royalty fees, to use, copy, modify, and distribute this
8  * software and its documentation for any purpose, provided that the
9  * above copyright notice and the following two paragraphs appear in
10  * all copies of this software.
11  *
12  * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
13  * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
14  * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
15  * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
16  * DAMAGE.
17  *
18  * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
19  * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
20  * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
21  * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
22  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
23  *
24  * Google Author(s): Behdad Esfahbod
25  */
26
27 #include "hb.hh"
28
29 #ifndef HB_NO_OT_SHAPE
30
31 #include "hb-ot-shaper-myanmar-machine.hh"
32 #include "hb-ot-shaper-indic.hh"
33 #include "hb-ot-layout.hh"
34
35
36 /*
37  * Myanmar shaper.
38  */
39
40
41 static const hb_tag_t
42 myanmar_basic_features[] =
43 {
44   /*
45    * Basic features.
46    * These features are applied in order, one at a time, after reordering,
47    * constrained to the syllable.
48    */
49   HB_TAG('r','p','h','f'),
50   HB_TAG('p','r','e','f'),
51   HB_TAG('b','l','w','f'),
52   HB_TAG('p','s','t','f'),
53 };
54 static const hb_tag_t
55 myanmar_other_features[] =
56 {
57   /*
58    * Other features.
59    * These features are applied all at once, after clearing syllables.
60    */
61   HB_TAG('p','r','e','s'),
62   HB_TAG('a','b','v','s'),
63   HB_TAG('b','l','w','s'),
64   HB_TAG('p','s','t','s'),
65 };
66
67 static inline void
68 set_myanmar_properties (hb_glyph_info_t &info)
69 {
70   hb_codepoint_t u = info.codepoint;
71   unsigned int type = hb_indic_get_categories (u);
72
73   info.myanmar_category() = (myanmar_category_t) (type & 0xFFu);
74 }
75
76
77 static inline bool
78 is_one_of_myanmar (const hb_glyph_info_t &info, unsigned int flags)
79 {
80   /* If it ligated, all bets are off. */
81   if (_hb_glyph_info_ligated (&info)) return false;
82   return !!(FLAG_UNSAFE (info.myanmar_category()) & flags);
83 }
84
85 /* Note:
86  *
87  * We treat Vowels and placeholders as if they were consonants.  This is safe because Vowels
88  * cannot happen in a consonant syllable.  The plus side however is, we can call the
89  * consonant syllable logic from the vowel syllable function and get it all right!
90  *
91  * Keep in sync with consonant_categories in the generator. */
92 #define CONSONANT_FLAGS_MYANMAR (FLAG (M_Cat(C)) | FLAG (M_Cat(CS)) | FLAG (M_Cat(Ra)) | /* FLAG (M_Cat(CM)) | */ FLAG (M_Cat(IV)) | FLAG (M_Cat(GB)) | FLAG (M_Cat(DOTTEDCIRCLE)))
93
94 static inline bool
95 is_consonant_myanmar (const hb_glyph_info_t &info)
96 {
97   return is_one_of_myanmar (info, CONSONANT_FLAGS_MYANMAR);
98 }
99
100
101 static bool
102 setup_syllables_myanmar (const hb_ot_shape_plan_t *plan,
103                          hb_font_t *font,
104                          hb_buffer_t *buffer);
105 static bool
106 reorder_myanmar (const hb_ot_shape_plan_t *plan,
107                  hb_font_t *font,
108                  hb_buffer_t *buffer);
109
110 static void
111 collect_features_myanmar (hb_ot_shape_planner_t *plan)
112 {
113   hb_ot_map_builder_t *map = &plan->map;
114
115   /* Do this before any lookups have been applied. */
116   map->add_gsub_pause (setup_syllables_myanmar);
117
118   map->enable_feature (HB_TAG('l','o','c','l'), F_PER_SYLLABLE);
119   /* The Indic specs do not require ccmp, but we apply it here since if
120    * there is a use of it, it's typically at the beginning. */
121   map->enable_feature (HB_TAG('c','c','m','p'), F_PER_SYLLABLE);
122
123
124   map->add_gsub_pause (reorder_myanmar);
125
126   for (unsigned int i = 0; i < ARRAY_LENGTH (myanmar_basic_features); i++)
127   {
128     map->enable_feature (myanmar_basic_features[i], F_MANUAL_ZWJ | F_PER_SYLLABLE);
129     map->add_gsub_pause (nullptr);
130   }
131   map->add_gsub_pause (hb_syllabic_clear_var); // Don't need syllables anymore, use stop to free buffer var
132
133   for (unsigned int i = 0; i < ARRAY_LENGTH (myanmar_other_features); i++)
134     map->enable_feature (myanmar_other_features[i], F_MANUAL_ZWJ);
135 }
136
137 static void
138 setup_masks_myanmar (const hb_ot_shape_plan_t *plan HB_UNUSED,
139                      hb_buffer_t              *buffer,
140                      hb_font_t                *font HB_UNUSED)
141 {
142   HB_BUFFER_ALLOCATE_VAR (buffer, myanmar_category);
143   HB_BUFFER_ALLOCATE_VAR (buffer, myanmar_position);
144
145   /* No masks, we just save information about characters. */
146
147   unsigned int count = buffer->len;
148   hb_glyph_info_t *info = buffer->info;
149   for (unsigned int i = 0; i < count; i++)
150     set_myanmar_properties (info[i]);
151 }
152
153 static bool
154 setup_syllables_myanmar (const hb_ot_shape_plan_t *plan HB_UNUSED,
155                          hb_font_t *font HB_UNUSED,
156                          hb_buffer_t *buffer)
157 {
158   HB_BUFFER_ALLOCATE_VAR (buffer, syllable);
159   find_syllables_myanmar (buffer);
160   foreach_syllable (buffer, start, end)
161     buffer->unsafe_to_break (start, end);
162   return false;
163 }
164
165 static int
166 compare_myanmar_order (const hb_glyph_info_t *pa, const hb_glyph_info_t *pb)
167 {
168   int a = pa->myanmar_position();
169   int b = pb->myanmar_position();
170
171   return (int) a - (int) b;
172 }
173
174
175 /* Rules from:
176  * https://docs.microsoft.com/en-us/typography/script-development/myanmar */
177
178 static void
179 initial_reordering_consonant_syllable (hb_buffer_t *buffer,
180                                        unsigned int start, unsigned int end)
181 {
182   hb_glyph_info_t *info = buffer->info;
183
184   unsigned int base = end;
185   bool has_reph = false;
186
187   {
188     unsigned int limit = start;
189     if (start + 3 <= end &&
190         info[start  ].myanmar_category() == M_Cat(Ra) &&
191         info[start+1].myanmar_category() == M_Cat(As) &&
192         info[start+2].myanmar_category() == M_Cat(H))
193     {
194       limit += 3;
195       base = start;
196       has_reph = true;
197     }
198
199     {
200       if (!has_reph)
201         base = limit;
202
203       for (unsigned int i = limit; i < end; i++)
204         if (is_consonant_myanmar (info[i]))
205         {
206           base = i;
207           break;
208         }
209     }
210   }
211
212   /* Reorder! */
213   {
214     unsigned int i = start;
215     for (; i < start + (has_reph ? 3 : 0); i++)
216       info[i].myanmar_position() = POS_AFTER_MAIN;
217     for (; i < base; i++)
218       info[i].myanmar_position() = POS_PRE_C;
219     if (i < end)
220     {
221       info[i].myanmar_position() = POS_BASE_C;
222       i++;
223     }
224     myanmar_position_t pos = POS_AFTER_MAIN;
225     /* The following loop may be ugly, but it implements all of
226      * Myanmar reordering! */
227     for (; i < end; i++)
228     {
229       if (info[i].myanmar_category() == M_Cat(MR)) /* Pre-base reordering */
230       {
231         info[i].myanmar_position() = POS_PRE_C;
232         continue;
233       }
234       if (info[i].myanmar_category() == M_Cat(VPre)) /* Left matra */
235       {
236         info[i].myanmar_position() = POS_PRE_M;
237         continue;
238       }
239       if (info[i].myanmar_category() == M_Cat(VS))
240       {
241         info[i].myanmar_position() = info[i - 1].myanmar_position();
242         continue;
243       }
244
245       if (pos == POS_AFTER_MAIN && info[i].myanmar_category() == M_Cat(VBlw))
246       {
247         pos = POS_BELOW_C;
248         info[i].myanmar_position() = pos;
249         continue;
250       }
251
252       if (pos == POS_BELOW_C && info[i].myanmar_category() == M_Cat(A))
253       {
254         info[i].myanmar_position() = POS_BEFORE_SUB;
255         continue;
256       }
257       if (pos == POS_BELOW_C && info[i].myanmar_category() == M_Cat(VBlw))
258       {
259         info[i].myanmar_position() = pos;
260         continue;
261       }
262       if (pos == POS_BELOW_C && info[i].myanmar_category() != M_Cat(A))
263       {
264         pos = POS_AFTER_SUB;
265         info[i].myanmar_position() = pos;
266         continue;
267       }
268       info[i].myanmar_position() = pos;
269     }
270   }
271
272   /* Sit tight, rock 'n roll! */
273   buffer->sort (start, end, compare_myanmar_order);
274
275   /* Flip left-matra sequence. */
276   unsigned first_left_matra = end;
277   unsigned last_left_matra = end;
278   for (unsigned int i = start; i < end; i++)
279   {
280     if (info[i].myanmar_position() == POS_PRE_M)
281     {
282       if (first_left_matra == end)
283         first_left_matra = i;
284       last_left_matra = i;
285     }
286   }
287   /* https://github.com/harfbuzz/harfbuzz/issues/3863 */
288   if (first_left_matra < last_left_matra)
289   {
290     /* No need to merge clusters, done already? */
291     buffer->reverse_range (first_left_matra, last_left_matra + 1);
292     /* Reverse back VS, etc. */
293     unsigned i = first_left_matra;
294     for (unsigned j = i; j <= last_left_matra; j++)
295       if (info[j].myanmar_category() == M_Cat(VPre))
296       {
297         buffer->reverse_range (i, j + 1);
298         i = j + 1;
299       }
300   }
301 }
302
303 static void
304 reorder_syllable_myanmar (const hb_ot_shape_plan_t *plan HB_UNUSED,
305                           hb_face_t *face HB_UNUSED,
306                           hb_buffer_t *buffer,
307                           unsigned int start, unsigned int end)
308 {
309   myanmar_syllable_type_t syllable_type = (myanmar_syllable_type_t) (buffer->info[start].syllable() & 0x0F);
310   switch (syllable_type) {
311
312     case myanmar_broken_cluster: /* We already inserted dotted-circles, so just call the consonant_syllable. */
313     case myanmar_consonant_syllable:
314       initial_reordering_consonant_syllable  (buffer, start, end);
315       break;
316
317     case myanmar_non_myanmar_cluster:
318       break;
319   }
320 }
321
322 static bool
323 reorder_myanmar (const hb_ot_shape_plan_t *plan,
324                  hb_font_t *font,
325                  hb_buffer_t *buffer)
326 {
327   bool ret = false;
328   if (buffer->message (font, "start reordering myanmar"))
329   {
330     if (hb_syllabic_insert_dotted_circles (font, buffer,
331                                            myanmar_broken_cluster,
332                                            M_Cat(DOTTEDCIRCLE)))
333       ret = true;
334
335     foreach_syllable (buffer, start, end)
336       reorder_syllable_myanmar (plan, font->face, buffer, start, end);
337     (void) buffer->message (font, "end reordering myanmar");
338   }
339
340   HB_BUFFER_DEALLOCATE_VAR (buffer, myanmar_category);
341   HB_BUFFER_DEALLOCATE_VAR (buffer, myanmar_position);
342
343   return ret;
344 }
345
346
347 const hb_ot_shaper_t _hb_ot_shaper_myanmar =
348 {
349   collect_features_myanmar,
350   nullptr, /* override_features */
351   nullptr, /* data_create */
352   nullptr, /* data_destroy */
353   nullptr, /* preprocess_text */
354   nullptr, /* postprocess_glyphs */
355   nullptr, /* decompose */
356   nullptr, /* compose */
357   setup_masks_myanmar,
358   nullptr, /* reorder_marks */
359   HB_TAG_NONE, /* gpos_tag */
360   HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS_NO_SHORT_CIRCUIT,
361   HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_EARLY,
362   false, /* fallback_position */
363 };
364
365
366 #ifndef HB_NO_OT_SHAPER_MYANMAR_ZAWGYI
367 /* Ugly Zawgyi encoding.
368  * Disable all auto processing.
369  * https://github.com/harfbuzz/harfbuzz/issues/1162 */
370 const hb_ot_shaper_t _hb_ot_shaper_myanmar_zawgyi =
371 {
372   nullptr, /* collect_features */
373   nullptr, /* override_features */
374   nullptr, /* data_create */
375   nullptr, /* data_destroy */
376   nullptr, /* preprocess_text */
377   nullptr, /* postprocess_glyphs */
378   nullptr, /* decompose */
379   nullptr, /* compose */
380   nullptr, /* setup_masks */
381   nullptr, /* reorder_marks */
382   HB_TAG_NONE, /* gpos_tag */
383   HB_OT_SHAPE_NORMALIZATION_MODE_NONE,
384   HB_OT_SHAPE_ZERO_WIDTH_MARKS_NONE,
385   false, /* fallback_position */
386 };
387 #endif
388
389
390 #endif