Imported Upstream version 2.3.1
[platform/upstream/harfbuzz.git] / src / hb-ot-shape-complex-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-ot-shape-complex-myanmar.hh"
28
29
30 /*
31  * Myanmar shaper.
32  */
33
34 static const hb_tag_t
35 basic_features[] =
36 {
37   /*
38    * Basic features.
39    * These features are applied in order, one at a time, after reordering.
40    */
41   HB_TAG('r','p','h','f'),
42   HB_TAG('p','r','e','f'),
43   HB_TAG('b','l','w','f'),
44   HB_TAG('p','s','t','f'),
45 };
46 static const hb_tag_t
47 other_features[] =
48 {
49   /*
50    * Other features.
51    * These features are applied all at once, after clearing syllables.
52    */
53   HB_TAG('p','r','e','s'),
54   HB_TAG('a','b','v','s'),
55   HB_TAG('b','l','w','s'),
56   HB_TAG('p','s','t','s'),
57 };
58 static const hb_tag_t
59 positioning_features[] =
60 {
61   /*
62    * Positioning features.
63    * We don't care about the types.
64    */
65   HB_TAG('d','i','s','t'),
66   /* Pre-release version of Windows 8 Myanmar font had abvm,blwm
67    * features.  The released Windows 8 version of the font (as well
68    * as the released spec) used 'mark' instead.  The Windows 8
69    * shaper however didn't apply 'mark' but did apply 'mkmk'.
70    * Perhaps it applied abvm/blwm.  This was fixed in a Windows 8
71    * update, so now it applies mark/mkmk.  We are guessing that
72    * it still applies abvm/blwm too.
73    */
74   HB_TAG('a','b','v','m'),
75   HB_TAG('b','l','w','m'),
76 };
77
78 static void
79 setup_syllables (const hb_ot_shape_plan_t *plan,
80                  hb_font_t *font,
81                  hb_buffer_t *buffer);
82 static void
83 reorder (const hb_ot_shape_plan_t *plan,
84          hb_font_t *font,
85          hb_buffer_t *buffer);
86 static void
87 clear_syllables (const hb_ot_shape_plan_t *plan,
88                  hb_font_t *font,
89                  hb_buffer_t *buffer);
90
91 static void
92 collect_features_myanmar (hb_ot_shape_planner_t *plan)
93 {
94   hb_ot_map_builder_t *map = &plan->map;
95
96   /* Do this before any lookups have been applied. */
97   map->add_gsub_pause (setup_syllables);
98
99   map->enable_feature (HB_TAG('l','o','c','l'));
100   /* The Indic specs do not require ccmp, but we apply it here since if
101    * there is a use of it, it's typically at the beginning. */
102   map->enable_feature (HB_TAG('c','c','m','p'));
103
104
105   map->add_gsub_pause (reorder);
106
107   for (unsigned int i = 0; i < ARRAY_LENGTH (basic_features); i++)
108   {
109     map->enable_feature (basic_features[i], F_MANUAL_ZWJ);
110     map->add_gsub_pause (nullptr);
111   }
112
113   map->add_gsub_pause (clear_syllables);
114
115   for (unsigned int i = 0; i < ARRAY_LENGTH (other_features); i++)
116     map->enable_feature (other_features[i], F_MANUAL_ZWJ);
117
118   for (unsigned int i = 0; i < ARRAY_LENGTH (positioning_features); i++)
119     map->enable_feature (positioning_features[i]);
120 }
121
122 static void
123 override_features_myanmar (hb_ot_shape_planner_t *plan)
124 {
125   plan->map.disable_feature (HB_TAG('l','i','g','a'));
126 }
127
128
129 enum syllable_type_t {
130   consonant_syllable,
131   punctuation_cluster,
132   broken_cluster,
133   non_myanmar_cluster,
134 };
135
136 #include "hb-ot-shape-complex-myanmar-machine.hh"
137
138
139 static void
140 setup_masks_myanmar (const hb_ot_shape_plan_t *plan HB_UNUSED,
141                    hb_buffer_t              *buffer,
142                    hb_font_t                *font HB_UNUSED)
143 {
144   HB_BUFFER_ALLOCATE_VAR (buffer, myanmar_category);
145   HB_BUFFER_ALLOCATE_VAR (buffer, myanmar_position);
146
147   /* We cannot setup masks here.  We save information about characters
148    * and setup masks later on in a pause-callback. */
149
150   unsigned int count = buffer->len;
151   hb_glyph_info_t *info = buffer->info;
152   for (unsigned int i = 0; i < count; i++)
153     set_myanmar_properties (info[i]);
154 }
155
156 static void
157 setup_syllables (const hb_ot_shape_plan_t *plan HB_UNUSED,
158                  hb_font_t *font HB_UNUSED,
159                  hb_buffer_t *buffer)
160 {
161   find_syllables (buffer);
162   foreach_syllable (buffer, start, end)
163     buffer->unsafe_to_break (start, end);
164 }
165
166 static int
167 compare_myanmar_order (const hb_glyph_info_t *pa, const hb_glyph_info_t *pb)
168 {
169   int a = pa->myanmar_position();
170   int b = pb->myanmar_position();
171
172   return a < b ? -1 : a == b ? 0 : +1;
173 }
174
175
176 /* Rules from:
177  * https://docs.microsoft.com/en-us/typography/script-development/myanmar */
178
179 static void
180 initial_reordering_consonant_syllable (hb_buffer_t *buffer,
181                                        unsigned int start, unsigned int end)
182 {
183   hb_glyph_info_t *info = buffer->info;
184
185   unsigned int base = end;
186   bool has_reph = false;
187
188   {
189     unsigned int limit = start;
190     if (start + 3 <= end &&
191         info[start  ].myanmar_category() == OT_Ra &&
192         info[start+1].myanmar_category() == OT_As &&
193         info[start+2].myanmar_category() == OT_H)
194     {
195       limit += 3;
196       base = start;
197       has_reph = true;
198     }
199
200     {
201       if (!has_reph)
202         base = limit;
203
204       for (unsigned int i = limit; i < end; i++)
205         if (is_consonant (info[i]))
206         {
207           base = i;
208           break;
209         }
210     }
211   }
212
213   /* Reorder! */
214   {
215     unsigned int i = start;
216     for (; i < start + (has_reph ? 3 : 0); i++)
217       info[i].myanmar_position() = POS_AFTER_MAIN;
218     for (; i < base; i++)
219       info[i].myanmar_position() = POS_PRE_C;
220     if (i < end)
221     {
222       info[i].myanmar_position() = POS_BASE_C;
223       i++;
224     }
225     indic_position_t pos = POS_AFTER_MAIN;
226     /* The following loop may be ugly, but it implements all of
227      * Myanmar reordering! */
228     for (; i < end; i++)
229     {
230       if (info[i].myanmar_category() == OT_MR) /* Pre-base reordering */
231       {
232         info[i].myanmar_position() = POS_PRE_C;
233         continue;
234       }
235       if (info[i].myanmar_position() < POS_BASE_C) /* Left matra */
236       {
237         continue;
238       }
239       if (info[i].myanmar_category() == OT_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() == OT_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() == OT_A)
253       {
254         info[i].myanmar_position() = POS_BEFORE_SUB;
255         continue;
256       }
257       if (pos == POS_BELOW_C && info[i].myanmar_category() == OT_VBlw)
258       {
259         info[i].myanmar_position() = pos;
260         continue;
261       }
262       if (pos == POS_BELOW_C && info[i].myanmar_category() != OT_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
276 static void
277 initial_reordering_syllable (const hb_ot_shape_plan_t *plan HB_UNUSED,
278                              hb_face_t *face HB_UNUSED,
279                              hb_buffer_t *buffer,
280                              unsigned int start, unsigned int end)
281 {
282   syllable_type_t syllable_type = (syllable_type_t) (buffer->info[start].syllable() & 0x0F);
283   switch (syllable_type) {
284
285     case broken_cluster: /* We already inserted dotted-circles, so just call the consonant_syllable. */
286     case consonant_syllable:
287       initial_reordering_consonant_syllable  (buffer, start, end);
288       break;
289
290     case punctuation_cluster:
291     case non_myanmar_cluster:
292       break;
293   }
294 }
295
296 static inline void
297 insert_dotted_circles (const hb_ot_shape_plan_t *plan HB_UNUSED,
298                        hb_font_t *font,
299                        hb_buffer_t *buffer)
300 {
301   /* Note: This loop is extra overhead, but should not be measurable. */
302   bool has_broken_syllables = false;
303   unsigned int count = buffer->len;
304   hb_glyph_info_t *info = buffer->info;
305   for (unsigned int i = 0; i < count; i++)
306     if ((info[i].syllable() & 0x0F) == broken_cluster)
307     {
308       has_broken_syllables = true;
309       break;
310     }
311   if (likely (!has_broken_syllables))
312     return;
313
314
315   hb_codepoint_t dottedcircle_glyph;
316   if (!font->get_nominal_glyph (0x25CCu, &dottedcircle_glyph))
317     return;
318
319   hb_glyph_info_t dottedcircle = {0};
320   dottedcircle.codepoint = 0x25CCu;
321   set_myanmar_properties (dottedcircle);
322   dottedcircle.codepoint = dottedcircle_glyph;
323
324   buffer->clear_output ();
325
326   buffer->idx = 0;
327   unsigned int last_syllable = 0;
328   while (buffer->idx < buffer->len && buffer->successful)
329   {
330     unsigned int syllable = buffer->cur().syllable();
331     syllable_type_t syllable_type = (syllable_type_t) (syllable & 0x0F);
332     if (unlikely (last_syllable != syllable && syllable_type == broken_cluster))
333     {
334       last_syllable = syllable;
335
336       hb_glyph_info_t ginfo = dottedcircle;
337       ginfo.cluster = buffer->cur().cluster;
338       ginfo.mask = buffer->cur().mask;
339       ginfo.syllable() = buffer->cur().syllable();
340
341       buffer->output_info (ginfo);
342     }
343     else
344       buffer->next_glyph ();
345   }
346   buffer->swap_buffers ();
347 }
348
349 static void
350 reorder (const hb_ot_shape_plan_t *plan,
351          hb_font_t *font,
352          hb_buffer_t *buffer)
353 {
354   insert_dotted_circles (plan, font, buffer);
355
356   foreach_syllable (buffer, start, end)
357     initial_reordering_syllable (plan, font->face, buffer, start, end);
358
359   HB_BUFFER_DEALLOCATE_VAR (buffer, myanmar_category);
360   HB_BUFFER_DEALLOCATE_VAR (buffer, myanmar_position);
361 }
362
363 static void
364 clear_syllables (const hb_ot_shape_plan_t *plan HB_UNUSED,
365                  hb_font_t *font HB_UNUSED,
366                  hb_buffer_t *buffer)
367 {
368   hb_glyph_info_t *info = buffer->info;
369   unsigned int count = buffer->len;
370   for (unsigned int i = 0; i < count; i++)
371     info[i].syllable() = 0;
372 }
373
374
375 const hb_ot_complex_shaper_t _hb_ot_complex_shaper_myanmar =
376 {
377   collect_features_myanmar,
378   override_features_myanmar,
379   nullptr, /* data_create */
380   nullptr, /* data_destroy */
381   nullptr, /* preprocess_text */
382   nullptr, /* postprocess_glyphs */
383   HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS_NO_SHORT_CIRCUIT,
384   nullptr, /* decompose */
385   nullptr, /* compose */
386   setup_masks_myanmar,
387   HB_TAG_NONE, /* gpos_tag */
388   nullptr, /* reorder_marks */
389   HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF_EARLY,
390   false, /* fallback_position */
391 };
392
393
394 /* Ugly Zawgyi encoding.
395  * Disable all auto processing.
396  * https://github.com/harfbuzz/harfbuzz/issues/1162 */
397 const hb_ot_complex_shaper_t _hb_ot_complex_shaper_myanmar_zawgyi =
398 {
399   nullptr, /* collect_features */
400   nullptr, /* override_features */
401   nullptr, /* data_create */
402   nullptr, /* data_destroy */
403   nullptr, /* preprocess_text */
404   nullptr, /* postprocess_glyphs */
405   HB_OT_SHAPE_NORMALIZATION_MODE_NONE,
406   nullptr, /* decompose */
407   nullptr, /* compose */
408   nullptr, /* setup_masks */
409   HB_TAG_NONE, /* gpos_tag */
410   nullptr, /* reorder_marks */
411   HB_OT_SHAPE_ZERO_WIDTH_MARKS_NONE,
412   false, /* fallback_position */
413 };