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