src/java.desktop/share/native/libfontmanager/harfbuzz/hb-ot-shape-complex-khmer.cc
changeset 54232 7c11a7cc7c1d
parent 51000 7c8841474f57
equal deleted inserted replaced
54231:e4813eded7cb 54232:7c11a7cc7c1d
    22  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
    22  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
    23  *
    23  *
    24  * Google Author(s): Behdad Esfahbod
    24  * Google Author(s): Behdad Esfahbod
    25  */
    25  */
    26 
    26 
    27 #include "hb-ot-shape-complex-khmer-private.hh"
    27 #include "hb-ot-shape-complex-khmer.hh"
    28 #include "hb-ot-layout-private.hh"
    28 #include "hb-ot-layout.hh"
    29 
    29 
    30 
    30 
    31 /*
    31 /*
    32  * Khmer shaper.
    32  * Khmer shaper.
    33  */
    33  */
    34 
    34 
    35 struct feature_list_t {
    35 static const hb_ot_map_feature_t
    36   hb_tag_t tag;
       
    37   hb_ot_map_feature_flags_t flags;
       
    38 };
       
    39 
       
    40 static const feature_list_t
       
    41 khmer_features[] =
    36 khmer_features[] =
    42 {
    37 {
    43   /*
    38   /*
    44    * Basic features.
    39    * Basic features.
    45    * These features are applied in order, one at a time, after initial_reordering.
    40    * These features are applied in order, one at a time, after reordering.
    46    */
    41    */
    47   {HB_TAG('p','r','e','f'), F_NONE},
    42   {HB_TAG('p','r','e','f'), F_MANUAL_JOINERS},
    48   {HB_TAG('b','l','w','f'), F_NONE},
    43   {HB_TAG('b','l','w','f'), F_MANUAL_JOINERS},
    49   {HB_TAG('a','b','v','f'), F_NONE},
    44   {HB_TAG('a','b','v','f'), F_MANUAL_JOINERS},
    50   {HB_TAG('p','s','t','f'), F_NONE},
    45   {HB_TAG('p','s','t','f'), F_MANUAL_JOINERS},
    51   {HB_TAG('c','f','a','r'), F_NONE},
    46   {HB_TAG('c','f','a','r'), F_MANUAL_JOINERS},
    52   /*
    47   /*
    53    * Other features.
    48    * Other features.
    54    * These features are applied all at once, after final_reordering.
    49    * These features are applied all at once after clearing syllables.
    55    * Default Bengali font in Windows for example has intermixed
       
    56    * lookups for init,pres,abvs,blws features.
       
    57    */
    50    */
    58   {HB_TAG('p','r','e','s'), F_GLOBAL},
    51   {HB_TAG('p','r','e','s'), F_GLOBAL_MANUAL_JOINERS},
    59   {HB_TAG('a','b','v','s'), F_GLOBAL},
    52   {HB_TAG('a','b','v','s'), F_GLOBAL_MANUAL_JOINERS},
    60   {HB_TAG('b','l','w','s'), F_GLOBAL},
    53   {HB_TAG('b','l','w','s'), F_GLOBAL_MANUAL_JOINERS},
    61   {HB_TAG('p','s','t','s'), F_GLOBAL},
    54   {HB_TAG('p','s','t','s'), F_GLOBAL_MANUAL_JOINERS},
    62   /* Positioning features, though we don't care about the types. */
    55   /*
       
    56    * Positioning features.
       
    57    * We don't care about the types.
       
    58    */
    63   {HB_TAG('d','i','s','t'), F_GLOBAL},
    59   {HB_TAG('d','i','s','t'), F_GLOBAL},
    64   {HB_TAG('a','b','v','m'), F_GLOBAL},
    60   {HB_TAG('a','b','v','m'), F_GLOBAL},
    65   {HB_TAG('b','l','w','m'), F_GLOBAL},
    61   {HB_TAG('b','l','w','m'), F_GLOBAL},
    66 };
    62 };
    67 
    63 
    77 
    73 
    78   _PRES,
    74   _PRES,
    79   _ABVS,
    75   _ABVS,
    80   _BLWS,
    76   _BLWS,
    81   _PSTS,
    77   _PSTS,
       
    78 
    82   _DIST,
    79   _DIST,
    83   _ABVM,
    80   _ABVM,
    84   _BLWM,
    81   _BLWM,
    85 
    82 
    86   KHMER_NUM_FEATURES,
    83   KHMER_NUM_FEATURES,
    87   KHMER_BASIC_FEATURES = _PRES /* Don't forget to update this! */
    84   KHMER_BASIC_FEATURES = _PRES, /* Don't forget to update this! */
    88 };
    85 };
    89 
    86 
    90 static void
    87 static void
    91 setup_syllables (const hb_ot_shape_plan_t *plan,
    88 setup_syllables (const hb_ot_shape_plan_t *plan,
    92                  hb_font_t *font,
    89                  hb_font_t *font,
    93                  hb_buffer_t *buffer);
    90                  hb_buffer_t *buffer);
    94 static void
    91 static void
    95 initial_reordering (const hb_ot_shape_plan_t *plan,
    92 reorder (const hb_ot_shape_plan_t *plan,
    96                     hb_font_t *font,
    93          hb_font_t *font,
    97                     hb_buffer_t *buffer);
    94          hb_buffer_t *buffer);
    98 static void
       
    99 final_reordering (const hb_ot_shape_plan_t *plan,
       
   100                   hb_font_t *font,
       
   101                   hb_buffer_t *buffer);
       
   102 static void
    95 static void
   103 clear_syllables (const hb_ot_shape_plan_t *plan,
    96 clear_syllables (const hb_ot_shape_plan_t *plan,
   104                  hb_font_t *font,
    97                  hb_font_t *font,
   105                  hb_buffer_t *buffer);
    98                  hb_buffer_t *buffer);
   106 
    99 
   109 {
   102 {
   110   hb_ot_map_builder_t *map = &plan->map;
   103   hb_ot_map_builder_t *map = &plan->map;
   111 
   104 
   112   /* Do this before any lookups have been applied. */
   105   /* Do this before any lookups have been applied. */
   113   map->add_gsub_pause (setup_syllables);
   106   map->add_gsub_pause (setup_syllables);
   114 
   107   map->add_gsub_pause (reorder);
   115   map->add_global_bool_feature (HB_TAG('l','o','c','l'));
   108 
   116   /* The Indic specs do not require ccmp, but we apply it here since if
   109   /* Testing suggests that Uniscribe does NOT pause between basic
   117    * there is a use of it, it's typically at the beginning. */
   110    * features.  Test with KhmerUI.ttf and the following three
   118   map->add_global_bool_feature (HB_TAG('c','c','m','p'));
   111    * sequences:
   119 
   112    *
       
   113    *   U+1789,U+17BC
       
   114    *   U+1789,U+17D2,U+1789
       
   115    *   U+1789,U+17D2,U+1789,U+17BC
       
   116    *
       
   117    * https://github.com/harfbuzz/harfbuzz/issues/974
       
   118    */
       
   119   map->enable_feature (HB_TAG('l','o','c','l'));
       
   120   map->enable_feature (HB_TAG('c','c','m','p'));
   120 
   121 
   121   unsigned int i = 0;
   122   unsigned int i = 0;
   122   map->add_gsub_pause (initial_reordering);
   123   for (; i < KHMER_BASIC_FEATURES; i++)
   123   for (; i < KHMER_BASIC_FEATURES; i++) {
   124     map->add_feature (khmer_features[i]);
   124     map->add_feature (khmer_features[i].tag, 1, khmer_features[i].flags | F_MANUAL_ZWJ | F_MANUAL_ZWNJ);
       
   125     map->add_gsub_pause (nullptr);
       
   126   }
       
   127   map->add_gsub_pause (final_reordering);
       
   128   for (; i < KHMER_NUM_FEATURES; i++) {
       
   129     map->add_feature (khmer_features[i].tag, 1, khmer_features[i].flags | F_MANUAL_ZWJ | F_MANUAL_ZWNJ);
       
   130   }
       
   131 
       
   132   map->add_global_bool_feature (HB_TAG('c','a','l','t'));
       
   133   map->add_global_bool_feature (HB_TAG('c','l','i','g'));
       
   134 
   125 
   135   map->add_gsub_pause (clear_syllables);
   126   map->add_gsub_pause (clear_syllables);
       
   127 
       
   128   for (; i < KHMER_NUM_FEATURES; i++)
       
   129     map->add_feature (khmer_features[i]);
   136 }
   130 }
   137 
   131 
   138 static void
   132 static void
   139 override_features_khmer (hb_ot_shape_planner_t *plan)
   133 override_features_khmer (hb_ot_shape_planner_t *plan)
   140 {
   134 {
       
   135   hb_ot_map_builder_t *map = &plan->map;
       
   136 
       
   137   /* Khmer spec has 'clig' as part of required shaping features:
       
   138    * "Apply feature 'clig' to form ligatures that are desired for
       
   139    * typographical correctness.", hence in overrides... */
       
   140   map->enable_feature (HB_TAG('c','l','i','g'));
       
   141 
   141   /* Uniscribe does not apply 'kern' in Khmer. */
   142   /* Uniscribe does not apply 'kern' in Khmer. */
   142   if (hb_options ().uniscribe_bug_compatible)
   143   if (hb_options ().uniscribe_bug_compatible)
   143   {
   144   {
   144     plan->map.add_feature (HB_TAG('k','e','r','n'), 0, F_GLOBAL);
   145     map->disable_feature (HB_TAG('k','e','r','n'));
   145   }
   146   }
   146 
   147 
   147   plan->map.add_feature (HB_TAG('l','i','g','a'), 0, F_GLOBAL);
   148   map->disable_feature (HB_TAG('l','i','g','a'));
   148 }
   149 }
   149 
   150 
   150 
   151 
   151 struct would_substitute_feature_t
   152 struct would_substitute_feature_t
   152 {
   153 {
   153   inline void init (const hb_ot_map_t *map, hb_tag_t feature_tag, bool zero_context_)
   154   void init (const hb_ot_map_t *map, hb_tag_t feature_tag, bool zero_context_)
   154   {
   155   {
   155     zero_context = zero_context_;
   156     zero_context = zero_context_;
   156     map->get_stage_lookups (0/*GSUB*/,
   157     map->get_stage_lookups (0/*GSUB*/,
   157                             map->get_feature_stage (0/*GSUB*/, feature_tag),
   158                             map->get_feature_stage (0/*GSUB*/, feature_tag),
   158                             &lookups, &count);
   159                             &lookups, &count);
   159   }
   160   }
   160 
   161 
   161   inline bool would_substitute (const hb_codepoint_t *glyphs,
   162   bool would_substitute (const hb_codepoint_t *glyphs,
   162                                 unsigned int          glyphs_count,
   163                          unsigned int          glyphs_count,
   163                                 hb_face_t            *face) const
   164                          hb_face_t            *face) const
   164   {
   165   {
   165     for (unsigned int i = 0; i < count; i++)
   166     for (unsigned int i = 0; i < count; i++)
   166       if (hb_ot_layout_lookup_would_substitute_fast (face, lookups[i].index, glyphs, glyphs_count, zero_context))
   167       if (hb_ot_layout_lookup_would_substitute_fast (face, lookups[i].index, glyphs, glyphs_count, zero_context))
   167         return true;
   168         return true;
   168     return false;
   169     return false;
   174   bool zero_context;
   175   bool zero_context;
   175 };
   176 };
   176 
   177 
   177 struct khmer_shape_plan_t
   178 struct khmer_shape_plan_t
   178 {
   179 {
   179   ASSERT_POD ();
   180   bool get_virama_glyph (hb_font_t *font, hb_codepoint_t *pglyph) const
   180 
       
   181   inline bool get_virama_glyph (hb_font_t *font, hb_codepoint_t *pglyph) const
       
   182   {
   181   {
   183     hb_codepoint_t glyph = virama_glyph;
   182     hb_codepoint_t glyph = virama_glyph;
   184     if (unlikely (virama_glyph == (hb_codepoint_t) -1))
   183     if (unlikely (virama_glyph == (hb_codepoint_t) -1))
   185     {
   184     {
   186       if (!font->get_nominal_glyph (0x17D2u, &glyph))
   185       if (!font->get_nominal_glyph (0x17D2u, &glyph))
   241 setup_masks_khmer (const hb_ot_shape_plan_t *plan HB_UNUSED,
   240 setup_masks_khmer (const hb_ot_shape_plan_t *plan HB_UNUSED,
   242                    hb_buffer_t              *buffer,
   241                    hb_buffer_t              *buffer,
   243                    hb_font_t                *font HB_UNUSED)
   242                    hb_font_t                *font HB_UNUSED)
   244 {
   243 {
   245   HB_BUFFER_ALLOCATE_VAR (buffer, khmer_category);
   244   HB_BUFFER_ALLOCATE_VAR (buffer, khmer_category);
   246   HB_BUFFER_ALLOCATE_VAR (buffer, khmer_position);
       
   247 
   245 
   248   /* We cannot setup masks here.  We save information about characters
   246   /* We cannot setup masks here.  We save information about characters
   249    * and setup masks later on in a pause-callback. */
   247    * and setup masks later on in a pause-callback. */
   250 
   248 
   251   unsigned int count = buffer->len;
   249   unsigned int count = buffer->len;
   262   find_syllables (buffer);
   260   find_syllables (buffer);
   263   foreach_syllable (buffer, start, end)
   261   foreach_syllable (buffer, start, end)
   264     buffer->unsafe_to_break (start, end);
   262     buffer->unsafe_to_break (start, end);
   265 }
   263 }
   266 
   264 
   267 static int
       
   268 compare_khmer_order (const hb_glyph_info_t *pa, const hb_glyph_info_t *pb)
       
   269 {
       
   270   int a = pa->khmer_position();
       
   271   int b = pb->khmer_position();
       
   272 
       
   273   return a < b ? -1 : a == b ? 0 : +1;
       
   274 }
       
   275 
       
   276 
   265 
   277 /* Rules from:
   266 /* Rules from:
   278  * https://docs.microsoft.com/en-us/typography/script-development/devanagari */
   267  * https://docs.microsoft.com/en-us/typography/script-development/devanagari */
   279 
   268 
   280 static void
   269 static void
   281 initial_reordering_consonant_syllable (const hb_ot_shape_plan_t *plan,
   270 reorder_consonant_syllable (const hb_ot_shape_plan_t *plan,
   282                                        hb_face_t *face,
   271                             hb_face_t *face HB_UNUSED,
   283                                        hb_buffer_t *buffer,
   272                             hb_buffer_t *buffer,
   284                                        unsigned int start, unsigned int end)
   273                             unsigned int start, unsigned int end)
   285 {
   274 {
   286   const khmer_shape_plan_t *khmer_plan = (const khmer_shape_plan_t *) plan->data;
   275   const khmer_shape_plan_t *khmer_plan = (const khmer_shape_plan_t *) plan->data;
   287   hb_glyph_info_t *info = buffer->info;
   276   hb_glyph_info_t *info = buffer->info;
   288 
   277 
   289   /* 1. Khmer shaping assumes that a syllable will begin with a Cons, IndV, or Number. */
   278   /* Setup masks. */
   290 
   279   {
   291   /* The first consonant is always the base. */
   280     /* Post-base */
   292   unsigned int base = start;
   281     hb_mask_t mask = khmer_plan->mask_array[BLWF] | khmer_plan->mask_array[ABVF] | khmer_plan->mask_array[PSTF];
   293   info[base].khmer_position() = POS_BASE_C;
   282     for (unsigned int i = start + 1; i < end; i++)
   294 
   283       info[i].mask  |= mask;
   295   /* Mark all subsequent consonants as below. */
   284   }
   296   for (unsigned int i = base + 1; i < end; i++)
   285 
   297     if (is_consonant_or_vowel (info[i]))
   286   unsigned int num_coengs = 0;
   298       info[i].khmer_position() = POS_BELOW_C;
   287   for (unsigned int i = start + 1; i < end; i++)
   299 
   288   {
   300   /* Mark final consonants.  A final consonant is one appearing after a matra,
   289     /* """
   301    * like in Khmer. */
   290      * When a COENG + (Cons | IndV) combination are found (and subscript count
   302   for (unsigned int i = base + 1; i < end; i++)
   291      * is less than two) the character combination is handled according to the
   303     if (info[i].khmer_category() == OT_M) {
   292      * subscript type of the character following the COENG.
   304       for (unsigned int j = i + 1; j < end; j++)
   293      *
   305         if (is_consonant_or_vowel (info[j])) {
   294      * ...
   306           info[j].khmer_position() = POS_FINAL_C;
   295      *
   307           break;
   296      * Subscript Type 2 - The COENG + RO characters are reordered to immediately
   308         }
   297      * before the base glyph. Then the COENG + RO characters are assigned to have
   309       break;
   298      * the 'pref' OpenType feature applied to them.
   310     }
   299      * """
   311 
   300      */
   312   /* Attach misc marks to previous char to move with them. */
   301     if (info[i].khmer_category() == OT_Coeng && num_coengs <= 2 && i + 1 < end)
   313   {
       
   314     khmer_position_t last_pos = POS_START;
       
   315     for (unsigned int i = start; i < end; i++)
       
   316     {
   302     {
   317       if ((FLAG_UNSAFE (info[i].khmer_category()) & (JOINER_FLAGS | FLAG (OT_N) | FLAG (OT_RS) | MEDIAL_FLAGS | FLAG (OT_Coeng))))
   303       num_coengs++;
       
   304 
       
   305       if (info[i + 1].khmer_category() == OT_Ra)
   318       {
   306       {
   319         info[i].khmer_position() = last_pos;
   307         for (unsigned int j = 0; j < 2; j++)
   320         if (unlikely (info[i].khmer_category() == OT_Coeng &&
   308           info[i + j].mask |= khmer_plan->mask_array[PREF];
   321                       info[i].khmer_position() == POS_PRE_M))
   309 
   322         {
   310         /* Move the Coeng,Ro sequence to the start. */
   323           /*
   311         buffer->merge_clusters (start, i + 2);
   324            * Uniscribe doesn't move the Halant with Left Matra.
   312         hb_glyph_info_t t0 = info[i];
   325            * TEST: U+092B,U+093F,U+094DE
   313         hb_glyph_info_t t1 = info[i + 1];
   326            * We follow.  This is important for the Sinhala
   314         memmove (&info[start + 2], &info[start], (i - start) * sizeof (info[0]));
   327            * U+0DDA split matra since it decomposes to U+0DD9,U+0DCA
   315         info[start] = t0;
   328            * where U+0DD9 is a left matra and U+0DCA is the virama.
   316         info[start + 1] = t1;
   329            * We don't want to move the virama with the left matra.
       
   330            * TEST: U+0D9A,U+0DDA
       
   331            */
       
   332           for (unsigned int j = i; j > start; j--)
       
   333             if (info[j - 1].khmer_position() != POS_PRE_M) {
       
   334               info[i].khmer_position() = info[j - 1].khmer_position();
       
   335               break;
       
   336             }
       
   337         }
       
   338       } else if (info[i].khmer_position() != POS_SMVD) {
       
   339         last_pos = (khmer_position_t) info[i].khmer_position();
       
   340       }
       
   341     }
       
   342   }
       
   343   /* For post-base consonants let them own anything before them
       
   344    * since the last consonant or matra. */
       
   345   {
       
   346     unsigned int last = base;
       
   347     for (unsigned int i = base + 1; i < end; i++)
       
   348       if (is_consonant_or_vowel (info[i]))
       
   349       {
       
   350         for (unsigned int j = last + 1; j < i; j++)
       
   351           if (info[j].khmer_position() < POS_SMVD)
       
   352             info[j].khmer_position() = info[i].khmer_position();
       
   353         last = i;
       
   354       } else if (info[i].khmer_category() == OT_M)
       
   355         last = i;
       
   356   }
       
   357 
       
   358   {
       
   359     /* Use syllable() for sort accounting temporarily. */
       
   360     unsigned int syllable = info[start].syllable();
       
   361     for (unsigned int i = start; i < end; i++)
       
   362       info[i].syllable() = i - start;
       
   363 
       
   364     /* Sit tight, rock 'n roll! */
       
   365     hb_stable_sort (info + start, end - start, compare_khmer_order);
       
   366     /* Find base again */
       
   367     base = end;
       
   368     for (unsigned int i = start; i < end; i++)
       
   369       if (info[i].khmer_position() == POS_BASE_C)
       
   370       {
       
   371         base = i;
       
   372         break;
       
   373       }
       
   374 
       
   375     if (unlikely (end - start >= 127))
       
   376       buffer->merge_clusters (start, end);
       
   377     else
       
   378       /* Note!  syllable() is a one-byte field. */
       
   379       for (unsigned int i = base; i < end; i++)
       
   380         if (info[i].syllable() != 255)
       
   381         {
       
   382           unsigned int max = i;
       
   383           unsigned int j = start + info[i].syllable();
       
   384           while (j != i)
       
   385           {
       
   386             max = MAX (max, j);
       
   387             unsigned int next = start + info[j].syllable();
       
   388             info[j].syllable() = 255; /* So we don't process j later again. */
       
   389             j = next;
       
   390           }
       
   391           if (i != max)
       
   392             buffer->merge_clusters (i, max + 1);
       
   393         }
       
   394 
       
   395     /* Put syllable back in. */
       
   396     for (unsigned int i = start; i < end; i++)
       
   397       info[i].syllable() = syllable;
       
   398   }
       
   399 
       
   400   /* Setup masks now */
       
   401 
       
   402   {
       
   403     hb_mask_t mask;
       
   404 
       
   405     /* Post-base */
       
   406     mask = khmer_plan->mask_array[BLWF] | khmer_plan->mask_array[ABVF] | khmer_plan->mask_array[PSTF];
       
   407     for (unsigned int i = base + 1; i < end; i++)
       
   408       info[i].mask  |= mask;
       
   409   }
       
   410 
       
   411   unsigned int pref_len = 2;
       
   412   if (khmer_plan->mask_array[PREF] && base + pref_len < end)
       
   413   {
       
   414     /* Find a Halant,Ra sequence and mark it for pre-base-reordering processing. */
       
   415     for (unsigned int i = base + 1; i + pref_len - 1 < end; i++) {
       
   416       hb_codepoint_t glyphs[2];
       
   417       for (unsigned int j = 0; j < pref_len; j++)
       
   418         glyphs[j] = info[i + j].codepoint;
       
   419       if (khmer_plan->pref.would_substitute (glyphs, pref_len, face))
       
   420       {
       
   421         for (unsigned int j = 0; j < pref_len; j++)
       
   422           info[i++].mask |= khmer_plan->mask_array[PREF];
       
   423 
   317 
   424         /* Mark the subsequent stuff with 'cfar'.  Used in Khmer.
   318         /* Mark the subsequent stuff with 'cfar'.  Used in Khmer.
   425          * Read the feature spec.
   319          * Read the feature spec.
   426          * This allows distinguishing the following cases with MS Khmer fonts:
   320          * This allows distinguishing the following cases with MS Khmer fonts:
   427          * U+1784,U+17D2,U+179A,U+17D2,U+1782
   321          * U+1784,U+17D2,U+179A,U+17D2,U+1782
   428          * U+1784,U+17D2,U+1782,U+17D2,U+179A
   322          * U+1784,U+17D2,U+1782,U+17D2,U+179A
   429          */
   323          */
   430         if (khmer_plan->mask_array[CFAR])
   324         if (khmer_plan->mask_array[CFAR])
   431           for (; i < end; i++)
   325           for (unsigned int j = i + 2; j < end; j++)
   432             info[i].mask |= khmer_plan->mask_array[CFAR];
   326             info[j].mask |= khmer_plan->mask_array[CFAR];
   433 
   327 
   434         break;
   328         num_coengs = 2; /* Done. */
   435       }
   329       }
       
   330     }
       
   331 
       
   332     /* Reorder left matra piece. */
       
   333     else if (info[i].khmer_category() == OT_VPre)
       
   334     {
       
   335       /* Move to the start. */
       
   336       buffer->merge_clusters (start, i + 1);
       
   337       hb_glyph_info_t t = info[i];
       
   338       memmove (&info[start + 1], &info[start], (i - start) * sizeof (info[0]));
       
   339       info[start] = t;
   436     }
   340     }
   437   }
   341   }
   438 }
   342 }
   439 
   343 
   440 static void
   344 static void
   446   syllable_type_t syllable_type = (syllable_type_t) (buffer->info[start].syllable() & 0x0F);
   350   syllable_type_t syllable_type = (syllable_type_t) (buffer->info[start].syllable() & 0x0F);
   447   switch (syllable_type)
   351   switch (syllable_type)
   448   {
   352   {
   449     case broken_cluster: /* We already inserted dotted-circles, so just call the consonant_syllable. */
   353     case broken_cluster: /* We already inserted dotted-circles, so just call the consonant_syllable. */
   450     case consonant_syllable:
   354     case consonant_syllable:
   451      initial_reordering_consonant_syllable (plan, face, buffer, start, end);
   355      reorder_consonant_syllable (plan, face, buffer, start, end);
   452      break;
   356      break;
   453 
   357 
   454     case non_khmer_cluster:
   358     case non_khmer_cluster:
   455       break;
   359       break;
   456   }
   360   }
   511       buffer->output_info (ginfo);
   415       buffer->output_info (ginfo);
   512     }
   416     }
   513     else
   417     else
   514       buffer->next_glyph ();
   418       buffer->next_glyph ();
   515   }
   419   }
   516 
       
   517   buffer->swap_buffers ();
   420   buffer->swap_buffers ();
   518 }
   421 }
   519 
   422 
   520 static void
   423 static void
   521 initial_reordering (const hb_ot_shape_plan_t *plan,
   424 reorder (const hb_ot_shape_plan_t *plan,
   522                     hb_font_t *font,
   425          hb_font_t *font,
   523                     hb_buffer_t *buffer)
   426          hb_buffer_t *buffer)
   524 {
   427 {
   525   insert_dotted_circles (plan, font, buffer);
   428   insert_dotted_circles (plan, font, buffer);
   526 
   429 
   527   foreach_syllable (buffer, start, end)
   430   foreach_syllable (buffer, start, end)
   528     initial_reordering_syllable (plan, font->face, buffer, start, end);
   431     initial_reordering_syllable (plan, font->face, buffer, start, end);
   529 }
       
   530 
       
   531 static void
       
   532 final_reordering_syllable (const hb_ot_shape_plan_t *plan,
       
   533                            hb_buffer_t *buffer,
       
   534                            unsigned int start, unsigned int end)
       
   535 {
       
   536   const khmer_shape_plan_t *khmer_plan = (const khmer_shape_plan_t *) plan->data;
       
   537   hb_glyph_info_t *info = buffer->info;
       
   538 
       
   539 
       
   540   /* This function relies heavily on halant glyphs.  Lots of ligation
       
   541    * and possibly multiple substitutions happened prior to this
       
   542    * phase, and that might have messed up our properties.  Recover
       
   543    * from a particular case of that where we're fairly sure that a
       
   544    * class of OT_Coeng is desired but has been lost. */
       
   545   if (khmer_plan->virama_glyph)
       
   546   {
       
   547     unsigned int virama_glyph = khmer_plan->virama_glyph;
       
   548     for (unsigned int i = start; i < end; i++)
       
   549       if (info[i].codepoint == virama_glyph &&
       
   550           _hb_glyph_info_ligated (&info[i]) &&
       
   551           _hb_glyph_info_multiplied (&info[i]))
       
   552       {
       
   553         /* This will make sure that this glyph passes is_coeng() test. */
       
   554         info[i].khmer_category() = OT_Coeng;
       
   555         _hb_glyph_info_clear_ligated_and_multiplied (&info[i]);
       
   556       }
       
   557   }
       
   558 
       
   559 
       
   560   /* 4. Final reordering:
       
   561    *
       
   562    * After the localized forms and basic shaping forms GSUB features have been
       
   563    * applied (see below), the shaping engine performs some final glyph
       
   564    * reordering before applying all the remaining font features to the entire
       
   565    * syllable.
       
   566    */
       
   567 
       
   568   bool try_pref = !!khmer_plan->mask_array[PREF];
       
   569 
       
   570   /* Find base again */
       
   571   unsigned int base;
       
   572   for (base = start; base < end; base++)
       
   573     if (info[base].khmer_position() >= POS_BASE_C)
       
   574     {
       
   575       if (try_pref && base + 1 < end)
       
   576       {
       
   577         for (unsigned int i = base + 1; i < end; i++)
       
   578           if ((info[i].mask & khmer_plan->mask_array[PREF]) != 0)
       
   579           {
       
   580             if (!(_hb_glyph_info_substituted (&info[i]) &&
       
   581                   _hb_glyph_info_ligated_and_didnt_multiply (&info[i])))
       
   582             {
       
   583               /* Ok, this was a 'pref' candidate but didn't form any.
       
   584                * Base is around here... */
       
   585               base = i;
       
   586               while (base < end && is_coeng (info[base]))
       
   587                 base++;
       
   588               info[base].khmer_position() = POS_BASE_C;
       
   589 
       
   590               try_pref = false;
       
   591             }
       
   592             break;
       
   593           }
       
   594       }
       
   595 
       
   596       if (start < base && info[base].khmer_position() > POS_BASE_C)
       
   597         base--;
       
   598       break;
       
   599     }
       
   600   if (base == end && start < base &&
       
   601       is_one_of (info[base - 1], FLAG (OT_ZWJ)))
       
   602     base--;
       
   603   if (base < end)
       
   604     while (start < base &&
       
   605            is_one_of (info[base], (FLAG (OT_N) | FLAG (OT_Coeng))))
       
   606       base--;
       
   607 
       
   608 
       
   609   /*   o Reorder matras:
       
   610    *
       
   611    *     If a pre-base matra character had been reordered before applying basic
       
   612    *     features, the glyph can be moved closer to the main consonant based on
       
   613    *     whether half-forms had been formed. Actual position for the matra is
       
   614    *     defined as “after last standalone halant glyph, after initial matra
       
   615    *     position and before the main consonant”. If ZWJ or ZWNJ follow this
       
   616    *     halant, position is moved after it.
       
   617    */
       
   618 
       
   619   if (start + 1 < end && start < base) /* Otherwise there can't be any pre-base matra characters. */
       
   620   {
       
   621     /* If we lost track of base, alas, position before last thingy. */
       
   622     unsigned int new_pos = base == end ? base - 2 : base - 1;
       
   623 
       
   624     while (new_pos > start &&
       
   625            !(is_one_of (info[new_pos], (FLAG (OT_M) | FLAG (OT_Coeng)))))
       
   626       new_pos--;
       
   627 
       
   628     /* If we found no Halant we are done.
       
   629      * Otherwise only proceed if the Halant does
       
   630      * not belong to the Matra itself! */
       
   631     if (is_coeng (info[new_pos]) &&
       
   632         info[new_pos].khmer_position() != POS_PRE_M)
       
   633     {
       
   634       /* -> If ZWJ or ZWNJ follow this halant, position is moved after it. */
       
   635       if (new_pos + 1 < end && is_joiner (info[new_pos + 1]))
       
   636         new_pos++;
       
   637     }
       
   638     else
       
   639       new_pos = start; /* No move. */
       
   640 
       
   641     if (start < new_pos && info[new_pos].khmer_position () != POS_PRE_M)
       
   642     {
       
   643       /* Now go see if there's actually any matras... */
       
   644       for (unsigned int i = new_pos; i > start; i--)
       
   645         if (info[i - 1].khmer_position () == POS_PRE_M)
       
   646         {
       
   647           unsigned int old_pos = i - 1;
       
   648           if (old_pos < base && base <= new_pos) /* Shouldn't actually happen. */
       
   649             base--;
       
   650 
       
   651           hb_glyph_info_t tmp = info[old_pos];
       
   652           memmove (&info[old_pos], &info[old_pos + 1], (new_pos - old_pos) * sizeof (info[0]));
       
   653           info[new_pos] = tmp;
       
   654 
       
   655           /* Note: this merge_clusters() is intentionally *after* the reordering.
       
   656            * Indic matra reordering is special and tricky... */
       
   657           buffer->merge_clusters (new_pos, MIN (end, base + 1));
       
   658 
       
   659           new_pos--;
       
   660         }
       
   661     } else {
       
   662       for (unsigned int i = start; i < base; i++)
       
   663         if (info[i].khmer_position () == POS_PRE_M) {
       
   664           buffer->merge_clusters (i, MIN (end, base + 1));
       
   665           break;
       
   666         }
       
   667     }
       
   668   }
       
   669 
       
   670 
       
   671   /*   o Reorder pre-base-reordering consonants:
       
   672    *
       
   673    *     If a pre-base-reordering consonant is found, reorder it according to
       
   674    *     the following rules:
       
   675    */
       
   676 
       
   677   if (try_pref && base + 1 < end) /* Otherwise there can't be any pre-base-reordering Ra. */
       
   678   {
       
   679     for (unsigned int i = base + 1; i < end; i++)
       
   680       if ((info[i].mask & khmer_plan->mask_array[PREF]) != 0)
       
   681       {
       
   682         /*       1. Only reorder a glyph produced by substitution during application
       
   683          *          of the <pref> feature. (Note that a font may shape a Ra consonant with
       
   684          *          the feature generally but block it in certain contexts.)
       
   685          */
       
   686         /* Note: We just check that something got substituted.  We don't check that
       
   687          * the <pref> feature actually did it...
       
   688          *
       
   689          * Reorder pref only if it ligated. */
       
   690         if (_hb_glyph_info_ligated_and_didnt_multiply (&info[i]))
       
   691         {
       
   692           /*
       
   693            *       2. Try to find a target position the same way as for pre-base matra.
       
   694            *          If it is found, reorder pre-base consonant glyph.
       
   695            *
       
   696            *       3. If position is not found, reorder immediately before main
       
   697            *          consonant.
       
   698            */
       
   699 
       
   700           unsigned int new_pos = base;
       
   701           while (new_pos > start &&
       
   702                  !(is_one_of (info[new_pos - 1], FLAG(OT_M) | FLAG (OT_Coeng))))
       
   703             new_pos--;
       
   704 
       
   705           /* In Khmer coeng model, a H,Ra can go *after* matras.  If it goes after a
       
   706            * split matra, it should be reordered to *before* the left part of such matra. */
       
   707           if (new_pos > start && info[new_pos - 1].khmer_category() == OT_M)
       
   708           {
       
   709             unsigned int old_pos = i;
       
   710             for (unsigned int j = base + 1; j < old_pos; j++)
       
   711               if (info[j].khmer_category() == OT_M)
       
   712               {
       
   713                 new_pos--;
       
   714                 break;
       
   715               }
       
   716           }
       
   717 
       
   718           if (new_pos > start && is_coeng (info[new_pos - 1]))
       
   719           {
       
   720             /* -> If ZWJ or ZWNJ follow this halant, position is moved after it. */
       
   721             if (new_pos < end && is_joiner (info[new_pos]))
       
   722               new_pos++;
       
   723           }
       
   724 
       
   725           {
       
   726             unsigned int old_pos = i;
       
   727 
       
   728             buffer->merge_clusters (new_pos, old_pos + 1);
       
   729             hb_glyph_info_t tmp = info[old_pos];
       
   730             memmove (&info[new_pos + 1], &info[new_pos], (old_pos - new_pos) * sizeof (info[0]));
       
   731             info[new_pos] = tmp;
       
   732 
       
   733             if (new_pos <= base && base < old_pos)
       
   734               base++;
       
   735           }
       
   736         }
       
   737 
       
   738         break;
       
   739       }
       
   740   }
       
   741 
       
   742 
       
   743   /*
       
   744    * Finish off the clusters and go home!
       
   745    */
       
   746   if (hb_options ().uniscribe_bug_compatible)
       
   747   {
       
   748     /* Uniscribe merges the entire syllable into a single cluster... Except for Tamil & Sinhala.
       
   749      * This means, half forms are submerged into the main consonant's cluster.
       
   750      * This is unnecessary, and makes cursor positioning harder, but that's what
       
   751      * Uniscribe does. */
       
   752     buffer->merge_clusters (start, end);
       
   753   }
       
   754 }
       
   755 
       
   756 
       
   757 static void
       
   758 final_reordering (const hb_ot_shape_plan_t *plan,
       
   759                   hb_font_t *font HB_UNUSED,
       
   760                   hb_buffer_t *buffer)
       
   761 {
       
   762   unsigned int count = buffer->len;
       
   763   if (unlikely (!count)) return;
       
   764 
       
   765   foreach_syllable (buffer, start, end)
       
   766     final_reordering_syllable (plan, buffer, start, end);
       
   767 
   432 
   768   HB_BUFFER_DEALLOCATE_VAR (buffer, khmer_category);
   433   HB_BUFFER_DEALLOCATE_VAR (buffer, khmer_category);
   769   HB_BUFFER_DEALLOCATE_VAR (buffer, khmer_position);
   434 }
   770 }
       
   771 
       
   772 
   435 
   773 static void
   436 static void
   774 clear_syllables (const hb_ot_shape_plan_t *plan HB_UNUSED,
   437 clear_syllables (const hb_ot_shape_plan_t *plan HB_UNUSED,
   775                  hb_font_t *font HB_UNUSED,
   438                  hb_font_t *font HB_UNUSED,
   776                  hb_buffer_t *buffer)
   439                  hb_buffer_t *buffer)
   829   nullptr, /* postprocess_glyphs */
   492   nullptr, /* postprocess_glyphs */
   830   HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS_NO_SHORT_CIRCUIT,
   493   HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS_NO_SHORT_CIRCUIT,
   831   decompose_khmer,
   494   decompose_khmer,
   832   compose_khmer,
   495   compose_khmer,
   833   setup_masks_khmer,
   496   setup_masks_khmer,
   834   nullptr, /* disable_otl */
   497   HB_TAG_NONE, /* gpos_tag */
   835   nullptr, /* reorder_marks */
   498   nullptr, /* reorder_marks */
   836   HB_OT_SHAPE_ZERO_WIDTH_MARKS_NONE,
   499   HB_OT_SHAPE_ZERO_WIDTH_MARKS_NONE,
   837   false, /* fallback_position */
   500   false, /* fallback_position */
   838 };
   501 };