Upgrade to latest harfbuzz
[framework/uifw/harfbuzz.git] / src / hb-ot-shape-complex-indic.cc
1 /*
2  * Copyright © 2011,2012  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-indic-private.hh"
28 #include "hb-ot-shape-private.hh"
29
30 struct indic_options_t
31 {
32   int initialized : 1;
33   int uniscribe_bug_compatible : 1;
34 };
35
36 union indic_options_union_t {
37   int i;
38   indic_options_t opts;
39 };
40 ASSERT_STATIC (sizeof (int) == sizeof (indic_options_union_t));
41
42 static indic_options_union_t
43 indic_options_init (void)
44 {
45   indic_options_union_t u;
46   u.i = 0;
47   u.opts.initialized = 1;
48
49   char *c = getenv ("HB_OT_INDIC_OPTIONS");
50   u.opts.uniscribe_bug_compatible = c && strstr (c, "uniscribe-bug-compatible");
51
52   return u;
53 }
54
55 inline indic_options_t
56 indic_options (void)
57 {
58   static indic_options_union_t options;
59
60   if (unlikely (!options.i)) {
61     /* This is idempotent and threadsafe. */
62     options = indic_options_init ();
63   }
64
65   return options.opts;
66 }
67
68
69 static int
70 compare_codepoint (const void *pa, const void *pb)
71 {
72   hb_codepoint_t a = * (hb_codepoint_t *) pa;
73   hb_codepoint_t b = * (hb_codepoint_t *) pb;
74
75   return a < b ? -1 : a == b ? 0 : +1;
76 }
77
78 static indic_position_t
79 consonant_position (hb_codepoint_t u)
80 {
81   consonant_position_t *record;
82
83   record = (consonant_position_t *) bsearch (&u, consonant_positions,
84                                              ARRAY_LENGTH (consonant_positions),
85                                              sizeof (consonant_positions[0]),
86                                              compare_codepoint);
87
88   return record ? record->position : POS_BASE_C;
89 }
90
91 static bool
92 is_ra (hb_codepoint_t u)
93 {
94   return !!bsearch (&u, ra_chars,
95                     ARRAY_LENGTH (ra_chars),
96                     sizeof (ra_chars[0]),
97                     compare_codepoint);
98 }
99
100 static bool
101 is_joiner (const hb_glyph_info_t &info)
102 {
103   return !!(FLAG (info.indic_category()) & (FLAG (OT_ZWJ) | FLAG (OT_ZWNJ)));
104 }
105
106 static bool
107 is_consonant (const hb_glyph_info_t &info)
108 {
109   /* Note:
110    *
111    * We treat Vowels and placeholders as if they were consonants.  This is safe because Vowels
112    * cannot happen in a consonant syllable.  The plus side however is, we can call the
113    * consonant syllable logic from the vowel syllable function and get it all right! */
114   return !!(FLAG (info.indic_category()) & (FLAG (OT_C) | FLAG (OT_Ra) | FLAG (OT_V) | FLAG (OT_NBSP) | FLAG (OT_DOTTEDCIRCLE)));
115 }
116
117 struct feature_list_t {
118   hb_tag_t tag;
119   hb_bool_t is_global;
120 };
121
122 static const feature_list_t
123 indic_basic_features[] =
124 {
125   {HB_TAG('n','u','k','t'), true},
126   {HB_TAG('a','k','h','n'), false},
127   {HB_TAG('r','p','h','f'), false},
128   {HB_TAG('r','k','r','f'), true},
129   {HB_TAG('p','r','e','f'), false},
130   {HB_TAG('b','l','w','f'), false},
131   {HB_TAG('h','a','l','f'), false},
132   {HB_TAG('p','s','t','f'), false},
133   {HB_TAG('c','j','c','t'), false},
134   {HB_TAG('v','a','t','u'), true},
135 };
136
137 /* Same order as the indic_basic_features array */
138 enum {
139   _NUKT,
140   AKHN,
141   RPHF,
142   _RKRF,
143   PREF,
144   BLWF,
145   HALF,
146   PSTF,
147   CJCT,
148   VATU
149 };
150
151 static const feature_list_t
152 indic_other_features[] =
153 {
154   {HB_TAG('i','n','i','t'), false},
155   {HB_TAG('p','r','e','s'), true},
156   {HB_TAG('a','b','v','s'), true},
157   {HB_TAG('b','l','w','s'), true},
158   {HB_TAG('p','s','t','s'), true},
159   {HB_TAG('h','a','l','n'), true},
160
161   {HB_TAG('d','i','s','t'), true},
162   {HB_TAG('a','b','v','m'), true},
163   {HB_TAG('b','l','w','m'), true},
164 };
165
166 /* Same order as the indic_other_features array */
167 enum {
168   INIT
169 };
170
171
172 static void
173 initial_reordering (const hb_ot_map_t *map,
174                     hb_face_t *face,
175                     hb_buffer_t *buffer,
176                     void *user_data HB_UNUSED);
177 static void
178 final_reordering (const hb_ot_map_t *map,
179                   hb_face_t *face,
180                   hb_buffer_t *buffer,
181                   void *user_data HB_UNUSED);
182
183 void
184 _hb_ot_shape_complex_collect_features_indic (hb_ot_map_builder_t *map,
185                                              const hb_segment_properties_t *props HB_UNUSED)
186 {
187   map->add_bool_feature (HB_TAG('l','o','c','l'));
188   /* The Indic specs do not require ccmp, but we apply it here since if
189    * there is a use of it, it's typically at the beginning. */
190   map->add_bool_feature (HB_TAG('c','c','m','p'));
191
192   map->add_gsub_pause (initial_reordering, NULL);
193
194   for (unsigned int i = 0; i < ARRAY_LENGTH (indic_basic_features); i++) {
195     map->add_bool_feature (indic_basic_features[i].tag, indic_basic_features[i].is_global);
196     map->add_gsub_pause (NULL, NULL);
197   }
198
199   map->add_gsub_pause (final_reordering, NULL);
200
201   for (unsigned int i = 0; i < ARRAY_LENGTH (indic_other_features); i++) {
202     map->add_bool_feature (indic_other_features[i].tag, indic_other_features[i].is_global);
203     map->add_gsub_pause (NULL, NULL);
204   }
205 }
206
207
208 hb_ot_shape_normalization_mode_t
209 _hb_ot_shape_complex_normalization_preference_indic (void)
210 {
211   /* We want split matras decomposed by the common shaping logic. */
212   return HB_OT_SHAPE_NORMALIZATION_MODE_DECOMPOSED;
213 }
214
215
216 void
217 _hb_ot_shape_complex_setup_masks_indic (hb_ot_map_t *map HB_UNUSED,
218                                         hb_buffer_t *buffer,
219                                         hb_font_t *font HB_UNUSED)
220 {
221   HB_BUFFER_ALLOCATE_VAR (buffer, indic_category);
222   HB_BUFFER_ALLOCATE_VAR (buffer, indic_position);
223
224   /* We cannot setup masks here.  We save information about characters
225    * and setup masks later on in a pause-callback. */
226
227   unsigned int count = buffer->len;
228   for (unsigned int i = 0; i < count; i++)
229   {
230     hb_glyph_info_t &info = buffer->info[i];
231     unsigned int type = get_indic_categories (info.codepoint);
232
233     info.indic_category() = type & 0x0F;
234     info.indic_position() = type >> 4;
235
236     /* The spec says U+0952 is OT_A.  However, testing shows that Uniscribe
237      * treats U+0951..U+0952 all as OT_VD.
238      * TESTS:
239      * U+092E,U+0947,U+0952
240      * U+092E,U+0952,U+0947
241      * U+092E,U+0947,U+0951
242      * U+092E,U+0951,U+0947
243      * */
244     if (unlikely (hb_in_range<hb_codepoint_t> (info.codepoint, 0x0951, 0x0954)))
245       info.indic_category() = OT_VD;
246
247     if (info.indic_category() == OT_C) {
248       info.indic_position() = consonant_position (info.codepoint);
249       if (is_ra (info.codepoint))
250         info.indic_category() = OT_Ra;
251     } else if (info.indic_category() == OT_SM ||
252                info.indic_category() == OT_VD) {
253       info.indic_position() = POS_SMVD;
254     } else if (unlikely (info.codepoint == 0x200C))
255       info.indic_category() = OT_ZWNJ;
256     else if (unlikely (info.codepoint == 0x200D))
257       info.indic_category() = OT_ZWJ;
258     else if (unlikely (info.codepoint == 0x25CC))
259       info.indic_category() = OT_DOTTEDCIRCLE;
260   }
261 }
262
263 static int
264 compare_indic_order (const hb_glyph_info_t *pa, const hb_glyph_info_t *pb)
265 {
266   int a = pa->indic_position();
267   int b = pb->indic_position();
268
269   return a < b ? -1 : a == b ? 0 : +1;
270 }
271
272 /* Rules from:
273  * https://www.microsoft.com/typography/otfntdev/devanot/shaping.aspx */
274
275 static void
276 initial_reordering_consonant_syllable (const hb_ot_map_t *map, hb_buffer_t *buffer, hb_mask_t *mask_array,
277                                        unsigned int start, unsigned int end)
278 {
279   hb_glyph_info_t *info = buffer->info;
280
281
282   /* 1. Find base consonant:
283    *
284    * The shaping engine finds the base consonant of the syllable, using the
285    * following algorithm: starting from the end of the syllable, move backwards
286    * until a consonant is found that does not have a below-base or post-base
287    * form (post-base forms have to follow below-base forms), or that is not a
288    * pre-base reordering Ra, or arrive at the first consonant. The consonant
289    * stopped at will be the base.
290    *
291    *   o If the syllable starts with Ra + Halant (in a script that has Reph)
292    *     and has more than one consonant, Ra is excluded from candidates for
293    *     base consonants.
294    */
295
296   unsigned int base = end;
297   bool has_reph = false;
298
299   {
300     /* -> If the syllable starts with Ra + Halant (in a script that has Reph)
301      *    and has more than one consonant, Ra is excluded from candidates for
302      *    base consonants. */
303     unsigned int limit = start;
304     if (mask_array[RPHF] &&
305         start + 3 <= end &&
306         info[start].indic_category() == OT_Ra &&
307         info[start + 1].indic_category() == OT_H &&
308         !is_joiner (info[start + 2]))
309     {
310       limit += 2;
311       base = start;
312       has_reph = true;
313     };
314
315     /* -> starting from the end of the syllable, move backwards */
316     unsigned int i = end;
317     do {
318       i--;
319       /* -> until a consonant is found */
320       if (is_consonant (info[i]))
321       {
322         /* -> that does not have a below-base or post-base form
323          * (post-base forms have to follow below-base forms), */
324         if (info[i].indic_position() != POS_BELOW_C &&
325             info[i].indic_position() != POS_POST_C)
326         {
327           base = i;
328           break;
329         }
330
331         /* -> or that is not a pre-base reordering Ra,
332          *
333          * TODO
334          */
335
336         /* -> or arrive at the first consonant. The consonant stopped at will
337          * be the base. */
338         base = i;
339       }
340       else
341         if (is_joiner (info[i]))
342           break;
343     } while (i > limit);
344     if (base < start)
345       base = start; /* Just in case... */
346
347
348     /* -> If the syllable starts with Ra + Halant (in a script that has Reph)
349      *    and has more than one consonant, Ra is excluded from candidates for
350      *    base consonants. */
351     if (has_reph && base == start) {
352       /* Have no other consonant, so Reph is not formed and Ra becomes base. */
353       has_reph = false;
354     }
355   }
356
357
358   /* 2. Decompose and reorder Matras:
359    *
360    * Each matra and any syllable modifier sign in the cluster are moved to the
361    * appropriate position relative to the consonant(s) in the cluster. The
362    * shaping engine decomposes two- or three-part matras into their constituent
363    * parts before any repositioning. Matra characters are classified by which
364    * consonant in a conjunct they have affinity for and are reordered to the
365    * following positions:
366    *
367    *   o Before first half form in the syllable
368    *   o After subjoined consonants
369    *   o After post-form consonant
370    *   o After main consonant (for above marks)
371    *
372    * IMPLEMENTATION NOTES:
373    *
374    * The normalize() routine has already decomposed matras for us, so we don't
375    * need to worry about that.
376    */
377
378
379   /* 3.  Reorder marks to canonical order:
380    *
381    * Adjacent nukta and halant or nukta and vedic sign are always repositioned
382    * if necessary, so that the nukta is first.
383    *
384    * IMPLEMENTATION NOTES:
385    *
386    * We don't need to do this: the normalize() routine already did this for us.
387    */
388
389
390   /* Reorder characters */
391
392   for (unsigned int i = start; i < base; i++)
393     info[i].indic_position() = POS_PRE_C;
394   info[base].indic_position() = POS_BASE_C;
395
396   /* Handle beginning Ra */
397   if (has_reph)
398     info[start].indic_position() = POS_RA_TO_BECOME_REPH;
399
400   /* For old-style Indic script tags, move the first post-base Halant after
401    * last consonant. */
402   if ((map->get_chosen_script (0) & 0x000000FF) != '2') {
403     /* We should only do this for Indic scripts which have a version two I guess. */
404     for (unsigned int i = base + 1; i < end; i++)
405       if (info[i].indic_category() == OT_H) {
406         unsigned int j;
407         for (j = end - 1; j > i; j--)
408           if (is_consonant (info[j]))
409             break;
410         if (j > i) {
411           /* Move Halant to after last consonant. */
412           hb_glyph_info_t t = info[i];
413           memmove (&info[i], &info[i + 1], (j - i) * sizeof (info[0]));
414           info[j] = t;
415         }
416         break;
417       }
418   }
419
420   /* Attach ZWJ, ZWNJ, nukta, and halant to previous char to move with them. */
421   if (!indic_options ().uniscribe_bug_compatible)
422   {
423     /* Please update the Uniscribe branch when touching this! */
424     for (unsigned int i = start + 1; i < end; i++)
425       if ((FLAG (info[i].indic_category()) & (FLAG (OT_ZWNJ) | FLAG (OT_ZWJ) | FLAG (OT_N) | FLAG (OT_H))))
426         info[i].indic_position() = info[i - 1].indic_position();
427   } else {
428     /*
429      * Uniscribe doesn't move the Halant with Left Matra.
430      * TEST: U+092B,U+093F,U+094DE
431      */
432     /* Please update the non-Uniscribe branch when touching this! */
433     for (unsigned int i = start + 1; i < end; i++)
434       if ((FLAG (info[i].indic_category()) & (FLAG (OT_ZWNJ) | FLAG (OT_ZWJ) | FLAG (OT_N) | FLAG (OT_H)))) {
435         info[i].indic_position() = info[i - 1].indic_position();
436         if (info[i].indic_category() == OT_H && info[i].indic_position() == POS_PRE_M)
437           for (unsigned int j = i; j > start; j--)
438             if (info[j - 1].indic_position() != POS_PRE_M) {
439               info[i].indic_position() = info[j - 1].indic_position();
440               break;
441             }
442       }
443   }
444
445   /* We do bubble-sort, skip malicious clusters attempts */
446   if (end - start < 64)
447   {
448     /* Sit tight, rock 'n roll! */
449     hb_bubble_sort (info + start, end - start, compare_indic_order);
450     /* Find base again */
451     base = end;
452     for (unsigned int i = start; i < end; i++)
453       if (info[i].indic_position() == POS_BASE_C) {
454         base = i;
455         break;
456       }
457   }
458
459   /* Setup masks now */
460
461   {
462     hb_mask_t mask;
463
464     /* Reph */
465     for (unsigned int i = start; i < end && info[i].indic_position() == POS_RA_TO_BECOME_REPH; i++)
466       info[i].mask |= mask_array[RPHF];
467
468     /* Pre-base */
469     mask = mask_array[HALF] | mask_array[AKHN] | mask_array[CJCT];
470     for (unsigned int i = start; i < base; i++)
471       info[i].mask  |= mask;
472     /* Base */
473     mask = mask_array[AKHN] | mask_array[CJCT];
474     info[base].mask |= mask;
475     /* Post-base */
476     mask = mask_array[BLWF] | mask_array[PSTF] | mask_array[CJCT];
477     for (unsigned int i = base + 1; i < end; i++)
478       info[i].mask  |= mask;
479   }
480
481   /* Apply ZWJ/ZWNJ effects */
482   for (unsigned int i = start + 1; i < end; i++)
483     if (is_joiner (info[i])) {
484       bool non_joiner = info[i].indic_category() == OT_ZWNJ;
485       unsigned int j = i;
486
487       do {
488         j--;
489
490         info[j].mask &= ~mask_array[CJCT];
491         if (non_joiner)
492           info[j].mask &= ~mask_array[HALF];
493
494       } while (j > start && !is_consonant (info[j]));
495     }
496 }
497
498
499 static void
500 initial_reordering_vowel_syllable (const hb_ot_map_t *map,
501                                    hb_buffer_t *buffer,
502                                    hb_mask_t *mask_array,
503                                    unsigned int start, unsigned int end)
504 {
505   /* We made the vowels look like consonants.  So let's call the consonant logic! */
506   initial_reordering_consonant_syllable (map, buffer, mask_array, start, end);
507 }
508
509 static void
510 initial_reordering_standalone_cluster (const hb_ot_map_t *map,
511                                        hb_buffer_t *buffer,
512                                        hb_mask_t *mask_array,
513                                        unsigned int start, unsigned int end)
514 {
515   /* We treat NBSP/dotted-circle as if they are consonants, so we should just chain.
516    * Only if not in compatibility mode that is... */
517
518   if (indic_options ().uniscribe_bug_compatible)
519   {
520     /* For dotted-circle, this is what Uniscribe does:
521      * If dotted-circle is the last glyph, it just does nothing.
522      * Ie. It doesn't form Reph. */
523     if (buffer->info[end - 1].indic_category() == OT_DOTTEDCIRCLE)
524       return;
525   }
526
527   initial_reordering_consonant_syllable (map, buffer, mask_array, start, end);
528 }
529
530 static void
531 initial_reordering_non_indic (const hb_ot_map_t *map HB_UNUSED,
532                               hb_buffer_t *buffer HB_UNUSED,
533                               hb_mask_t *mask_array HB_UNUSED,
534                               unsigned int start HB_UNUSED, unsigned int end HB_UNUSED)
535 {
536   /* Nothing to do right now.  If we ever switch to using the output
537    * buffer in the reordering process, we'd need to next_glyph() here. */
538 }
539
540 #include "hb-ot-shape-complex-indic-machine.hh"
541
542 static void
543 initial_reordering (const hb_ot_map_t *map,
544                     hb_face_t *face HB_UNUSED,
545                     hb_buffer_t *buffer,
546                     void *user_data HB_UNUSED)
547 {
548   hb_mask_t mask_array[ARRAY_LENGTH (indic_basic_features)] = {0};
549   unsigned int num_masks = ARRAY_LENGTH (indic_basic_features);
550   for (unsigned int i = 0; i < num_masks; i++)
551     mask_array[i] = map->get_1_mask (indic_basic_features[i].tag);
552
553   find_syllables (map, buffer, mask_array);
554 }
555
556 static void
557 final_reordering_syllable (hb_buffer_t *buffer, hb_mask_t *mask_array,
558                            unsigned int start, unsigned int end)
559 {
560   hb_glyph_info_t *info = buffer->info;
561
562   /* 4. Final reordering:
563    *
564    * After the localized forms and basic shaping forms GSUB features have been
565    * applied (see below), the shaping engine performs some final glyph
566    * reordering before applying all the remaining font features to the entire
567    * cluster.
568    */
569
570   /* Find base again */
571   unsigned int base = end;
572   for (unsigned int i = start; i < end; i++)
573     if (info[i].indic_position() == POS_BASE_C) {
574       base = i;
575       break;
576     }
577
578   if (base == start) {
579     /* There's no Reph, and no left Matra to reposition.  Just merge the cluster
580      * and go home. */
581     buffer->merge_clusters (start, end);
582     return;
583   }
584
585   unsigned int start_of_last_cluster = base;
586
587   /*   o Reorder matras:
588    *
589    *     If a pre-base matra character had been reordered before applying basic
590    *     features, the glyph can be moved closer to the main consonant based on
591    *     whether half-forms had been formed. Actual position for the matra is
592    *     defined as “after last standalone halant glyph, after initial matra
593    *     position and before the main consonant”. If ZWJ or ZWNJ follow this
594    *     halant, position is moved after it.
595    */
596
597   {
598     unsigned int new_matra_pos = base - 1;
599     while (new_matra_pos > start &&
600            !(FLAG (info[new_matra_pos].indic_category()) & (FLAG (OT_M) | FLAG (OT_H))))
601       new_matra_pos--;
602     /* If we found no Halant we are done.  Otherwise only proceed if the Halant does
603      * not belong to the Matra itself! */
604     if (info[new_matra_pos].indic_category() == OT_H &&
605         info[new_matra_pos].indic_position() != POS_PRE_M) {
606       /* -> If ZWJ or ZWNJ follow this halant, position is moved after it. */
607       if (new_matra_pos + 1 < end && is_joiner (info[new_matra_pos + 1]))
608         new_matra_pos++;
609
610       /* Now go see if there's actually any matras... */
611       for (unsigned int i = new_matra_pos; i > start; i--)
612         if (info[i - 1].indic_position () == POS_PRE_M)
613         {
614           unsigned int old_matra_pos = i - 1;
615           hb_glyph_info_t matra = info[old_matra_pos];
616           memmove (&info[old_matra_pos], &info[old_matra_pos + 1], (new_matra_pos - old_matra_pos) * sizeof (info[0]));
617           info[new_matra_pos] = matra;
618           start_of_last_cluster = MIN (new_matra_pos, start_of_last_cluster);
619           new_matra_pos--;
620         }
621     }
622   }
623
624
625   /*   o Reorder reph:
626    *
627    *     Reph’s original position is always at the beginning of the syllable,
628    *     (i.e. it is not reordered at the character reordering stage). However,
629    *     it will be reordered according to the basic-forms shaping results.
630    *     Possible positions for reph, depending on the script, are; after main,
631    *     before post-base consonant forms, and after post-base consonant forms.
632    */
633
634   /* If there's anything after the Ra that has the REPH pos, it ought to be halant.
635    * Which means that the font has failed to ligate the Reph.  In which case, we
636    * shouldn't move. */
637   if (start + 1 < end &&
638       info[start].indic_position() == POS_RA_TO_BECOME_REPH &&
639       info[start + 1].indic_position() != POS_RA_TO_BECOME_REPH)
640   {
641       unsigned int new_reph_pos;
642
643      enum reph_position_t {
644        REPH_AFTER_MAIN,
645        REPH_BEFORE_SUBSCRIPT,
646        REPH_AFTER_SUBSCRIPT,
647        REPH_BEFORE_POSTSCRIPT,
648        REPH_AFTER_POSTSCRIPT
649      } reph_pos;
650
651      /* XXX Figure out old behavior too */
652      switch ((hb_tag_t) buffer->props.script)
653      {
654        case HB_SCRIPT_MALAYALAM:
655        case HB_SCRIPT_ORIYA:
656          reph_pos = REPH_AFTER_MAIN;
657          break;
658
659        case HB_SCRIPT_GURMUKHI:
660          reph_pos = REPH_BEFORE_SUBSCRIPT;
661          break;
662
663        case HB_SCRIPT_BENGALI:
664          reph_pos = REPH_AFTER_SUBSCRIPT;
665          break;
666
667        default:
668        case HB_SCRIPT_DEVANAGARI:
669        case HB_SCRIPT_GUJARATI:
670          reph_pos = REPH_BEFORE_POSTSCRIPT;
671          break;
672
673        case HB_SCRIPT_KANNADA:
674        case HB_SCRIPT_TAMIL:
675        case HB_SCRIPT_TELUGU:
676          reph_pos = REPH_AFTER_POSTSCRIPT;
677          break;
678      }
679
680     /*       1. If reph should be positioned after post-base consonant forms,
681      *          proceed to step 5.
682      */
683     if (reph_pos == REPH_AFTER_POSTSCRIPT)
684     {
685       goto reph_step_5;
686     }
687
688     /*       2. If the reph repositioning class is not after post-base: target
689      *          position is after the first explicit halant glyph between the
690      *          first post-reph consonant and last main consonant. If ZWJ or ZWNJ
691      *          are following this halant, position is moved after it. If such
692      *          position is found, this is the target position. Otherwise,
693      *          proceed to the next step.
694      *
695      *          Note: in old-implementation fonts, where classifications were
696      *          fixed in shaping engine, there was no case where reph position
697      *          will be found on this step.
698      */
699     {
700       new_reph_pos = start + 1;
701       while (new_reph_pos < base && info[new_reph_pos].indic_category() != OT_H)
702         new_reph_pos++;
703
704       if (new_reph_pos < base && info[new_reph_pos].indic_category() == OT_H) {
705         /* ->If ZWJ or ZWNJ are following this halant, position is moved after it. */
706         if (new_reph_pos + 1 < base && is_joiner (info[new_reph_pos + 1]))
707           new_reph_pos++;
708         goto reph_move;
709       }
710     }
711
712     /*       3. If reph should be repositioned after the main consonant: find the
713      *          first consonant not ligated with main, or find the first
714      *          consonant that is not a potential pre-base reordering Ra.
715      */
716     if (reph_pos == REPH_AFTER_MAIN)
717     {
718       /* XXX */
719     }
720
721     /*       4. If reph should be positioned before post-base consonant, find
722      *          first post-base classified consonant not ligated with main. If no
723      *          consonant is found, the target position should be before the
724      *          first matra, syllable modifier sign or vedic sign.
725      */
726     /* This is our take on what step 4 is trying to say (and failing, BADLY). */
727     if (reph_pos == REPH_AFTER_SUBSCRIPT)
728     {
729       new_reph_pos = base;
730       while (new_reph_pos < end &&
731              !( FLAG (info[new_reph_pos + 1].indic_position()) & (FLAG (POS_POST_C) | FLAG (POS_POST_M) | FLAG (POS_SMVD))))
732         new_reph_pos++;
733       if (new_reph_pos < end)
734         goto reph_move;
735     }
736
737     /*       5. If no consonant is found in steps 3 or 4, move reph to a position
738      *          immediately before the first post-base matra, syllable modifier
739      *          sign or vedic sign that has a reordering class after the intended
740      *          reph position. For example, if the reordering position for reph
741      *          is post-main, it will skip above-base matras that also have a
742      *          post-main position.
743      */
744     reph_step_5:
745     {
746       /* XXX */
747     }
748
749     /*       6. Otherwise, reorder reph to the end of the syllable.
750      */
751     {
752       new_reph_pos = end - 1;
753       while (new_reph_pos > start && info[new_reph_pos].indic_position() == POS_SMVD)
754         new_reph_pos--;
755
756       /*
757        * If the Reph is to be ending up after a Matra,Halant sequence,
758        * position it before that Halant so it can interact with the Matra.
759        * However, if it's a plain Consonant,Halant we shouldn't do that.
760        * Uniscribe doesn't do this.
761        * TEST: U+0930,U+094D,U+0915,U+094B,U+094D
762        */
763       if (!indic_options ().uniscribe_bug_compatible &&
764           unlikely (info[new_reph_pos].indic_category() == OT_H)) {
765         for (unsigned int i = base + 1; i < new_reph_pos; i++)
766           if (info[i].indic_category() == OT_M) {
767             /* Ok, got it. */
768             new_reph_pos--;
769           }
770       }
771       goto reph_move;
772     }
773
774     reph_move:
775     {
776       /* Move */
777       hb_glyph_info_t reph = info[start];
778       memmove (&info[start], &info[start + 1], (new_reph_pos - start) * sizeof (info[0]));
779       info[new_reph_pos] = reph;
780       start_of_last_cluster = start; /* Yay, one big cluster! */
781     }
782   }
783
784
785   /*   o Reorder pre-base reordering consonants:
786    *
787    *     If a pre-base reordering consonant is found, reorder it according to
788    *     the following rules:
789    *
790    *       1. Only reorder a glyph produced by substitution during application
791    *          of the feature. (Note that a font may shape a Ra consonant with
792    *          the feature generally but block it in certain contexts.)
793    *
794    *       2. Try to find a target position the same way as for pre-base matra.
795    *          If it is found, reorder pre-base consonant glyph.
796    *
797    *       3. If position is not found, reorder immediately before main
798    *          consonant.
799    */
800
801   /* TODO */
802
803
804
805   /* Apply 'init' to the Left Matra if it's a word start. */
806   if (info[start].indic_position () == POS_PRE_M &&
807       (!start ||
808        !(FLAG (_hb_glyph_info_get_general_category (&info[start - 1])) &
809          (FLAG (HB_UNICODE_GENERAL_CATEGORY_LOWERCASE_LETTER) |
810           FLAG (HB_UNICODE_GENERAL_CATEGORY_MODIFIER_LETTER) |
811           FLAG (HB_UNICODE_GENERAL_CATEGORY_OTHER_LETTER) |
812           FLAG (HB_UNICODE_GENERAL_CATEGORY_TITLECASE_LETTER) |
813           FLAG (HB_UNICODE_GENERAL_CATEGORY_UPPERCASE_LETTER) |
814           FLAG (HB_UNICODE_GENERAL_CATEGORY_SPACING_MARK) |
815           FLAG (HB_UNICODE_GENERAL_CATEGORY_ENCLOSING_MARK) |
816           FLAG (HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK)))))
817     info[start].mask |= mask_array[INIT];
818
819
820
821   /* Finish off the clusters and go home! */
822
823   if (!indic_options ().uniscribe_bug_compatible)
824   {
825     /* This is what Uniscribe does.  Ie. add cluster boundaries after Halant,ZWNJ.
826      * This means, half forms are submerged into the main consonants cluster.
827      * This is unnecessary, and makes cursor positioning harder, but that's what
828      * Uniscribe does. */
829     unsigned int cluster_start = start;
830     for (unsigned int i = start + 1; i < start_of_last_cluster; i++)
831       if (info[i - 1].indic_category() == OT_H && info[i].indic_category() == OT_ZWNJ) {
832         i++;
833         buffer->merge_clusters (cluster_start, i);
834         cluster_start = i;
835       }
836     start_of_last_cluster = cluster_start;
837   }
838
839   buffer->merge_clusters (start_of_last_cluster, end);
840 }
841
842
843 static void
844 final_reordering (const hb_ot_map_t *map,
845                   hb_face_t *face HB_UNUSED,
846                   hb_buffer_t *buffer,
847                   void *user_data HB_UNUSED)
848 {
849   unsigned int count = buffer->len;
850   if (!count) return;
851
852   hb_mask_t mask_array[ARRAY_LENGTH (indic_other_features)] = {0};
853   unsigned int num_masks = ARRAY_LENGTH (indic_other_features);
854   for (unsigned int i = 0; i < num_masks; i++)
855     mask_array[i] = map->get_1_mask (indic_other_features[i].tag);
856
857   hb_glyph_info_t *info = buffer->info;
858   unsigned int last = 0;
859   unsigned int last_syllable = info[0].syllable();
860   for (unsigned int i = 1; i < count; i++)
861     if (last_syllable != info[i].syllable()) {
862       final_reordering_syllable (buffer, mask_array, last, i);
863       last = i;
864       last_syllable = info[last].syllable();
865     }
866   final_reordering_syllable (buffer, mask_array, last, count);
867
868   HB_BUFFER_DEALLOCATE_VAR (buffer, indic_category);
869   HB_BUFFER_DEALLOCATE_VAR (buffer, indic_position);
870 }
871
872
873