Imported Upstream version 1.4.6
[platform/upstream/harfbuzz.git] / src / hb-ot-shape-normalize.cc
index 93dd00c..94a3d7d 100644 (file)
@@ -25,6 +25,7 @@
  */
 
 #include "hb-ot-shape-normalize-private.hh"
+#include "hb-ot-shape-complex-private.hh"
 #include "hb-ot-shape-private.hh"
 
 
  *     with previous base, use that.  This needs the itemizer to have this
  *     knowledge too.  We need to provide assistance to the itemizer.
  *
- *   - When a font does not support a character but supports its decomposition,
- *     well, use the decomposition (preferring the canonical decomposition, but
- *     falling back to the compatibility decomposition if necessary).  The
- *     compatibility decomposition is really nice to have, for characters like
- *     ellipsis, or various-sized space characters.
+ *   - When a font does not support a character but supports its canonical
+ *     decomposition, well, use the decomposition.
  *
  *   - The complex shapers can customize the compose and decompose functions to
  *     offload some of their requirements to the normalizer.  For example, the
  *     Indic shaper may want to disallow recomposing of two matras.
- *
- *   - We try compatibility decomposition if decomposing through canonical
- *     decomposition alone failed to find a sequence that the font supports.
- *     We don't try compatibility decomposition recursively during the canonical
- *     decomposition phase.  This has minimal impact.  There are only a handful
- *     of Greek letter that have canonical decompositions that include characters
- *     with compatibility decomposition.  Those can be found using this command:
- *
- *     egrep  "`echo -n ';('; grep ';<' UnicodeData.txt | cut -d';' -f1 | tr '\n' '|'; echo ') '`" UnicodeData.txt
  */
 
-static hb_bool_t
-decompose_func (hb_unicode_funcs_t *unicode,
-               hb_codepoint_t  ab,
-               hb_codepoint_t *a,
-               hb_codepoint_t *b)
+static bool
+decompose_unicode (const hb_ot_shape_normalize_context_t *c,
+                  hb_codepoint_t  ab,
+                  hb_codepoint_t *a,
+                  hb_codepoint_t *b)
 {
-  /* XXX FIXME, move these to complex shapers and propagage to normalizer.*/
-  switch (ab) {
-    case 0x0AC9  : return false;
-
-    case 0x0931  : return false;
-    case 0x0B94  : return false;
-
-    /* These ones have Unicode decompositions, but we do it
-     * this way to be close to what Uniscribe does. */
-    case 0x0DDA  : *a = 0x0DD9; *b= 0x0DDA; return true;
-    case 0x0DDC  : *a = 0x0DD9; *b= 0x0DDC; return true;
-    case 0x0DDD  : *a = 0x0DD9; *b= 0x0DDD; return true;
-    case 0x0DDE  : *a = 0x0DD9; *b= 0x0DDE; return true;
-
-    case 0x0F77  : *a = 0x0FB2; *b= 0x0F81; return true;
-    case 0x0F79  : *a = 0x0FB3; *b= 0x0F81; return true;
-    case 0x17BE  : *a = 0x17C1; *b= 0x17BE; return true;
-    case 0x17BF  : *a = 0x17C1; *b= 0x17BF; return true;
-    case 0x17C0  : *a = 0x17C1; *b= 0x17C0; return true;
-    case 0x17C4  : *a = 0x17C1; *b= 0x17C4; return true;
-    case 0x17C5  : *a = 0x17C1; *b= 0x17C5; return true;
-    case 0x1925  : *a = 0x1920; *b= 0x1923; return true;
-    case 0x1926  : *a = 0x1920; *b= 0x1924; return true;
-    case 0x1B3C  : *a = 0x1B42; *b= 0x1B3C; return true;
-    case 0x1112E  : *a = 0x11127; *b= 0x11131; return true;
-    case 0x1112F  : *a = 0x11127; *b= 0x11132; return true;
-#if 0
-    case 0x0B57  : *a = 0xno decomp, -> RIGHT; return true;
-    case 0x1C29  : *a = 0xno decomp, -> LEFT; return true;
-    case 0xA9C0  : *a = 0xno decomp, -> RIGHT; return true;
-    case 0x111BF  : *a = 0xno decomp, -> ABOVE; return true;
-#endif
-  }
-  return unicode->decompose (ab, a, b);
+  return (bool) c->unicode->decompose (ab, a, b);
 }
 
-static hb_bool_t
-compose_func (hb_unicode_funcs_t *unicode,
-             hb_codepoint_t  a,
-             hb_codepoint_t  b,
-             hb_codepoint_t *ab)
+static bool
+compose_unicode (const hb_ot_shape_normalize_context_t *c,
+                hb_codepoint_t  a,
+                hb_codepoint_t  b,
+                hb_codepoint_t *ab)
 {
-  /* XXX, this belongs to indic normalizer. */
-  if ((FLAG (unicode->general_category (a)) &
-       (FLAG (HB_UNICODE_GENERAL_CATEGORY_SPACING_MARK) |
-       FLAG (HB_UNICODE_GENERAL_CATEGORY_ENCLOSING_MARK) |
-       FLAG (HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK))))
-    return false;
-  /* XXX, add composition-exclusion exceptions to Indic shaper. */
-  if (a == 0x09AF && b == 0x09BC) { *ab = 0x09DF; return true; }
-
-  /* XXX, these belong to the hebew / default shaper. */
-  /* Hebrew presentation-form shaping.
-   * https://bugzilla.mozilla.org/show_bug.cgi?id=728866 */
-  // Hebrew presentation forms with dagesh, for characters 0x05D0..0x05EA;
-  // note that some letters do not have a dagesh presForm encoded
-  static const hb_codepoint_t sDageshForms[0x05EA - 0x05D0 + 1] = {
-    0xFB30, // ALEF
-    0xFB31, // BET
-    0xFB32, // GIMEL
-    0xFB33, // DALET
-    0xFB34, // HE
-    0xFB35, // VAV
-    0xFB36, // ZAYIN
-    0, // HET
-    0xFB38, // TET
-    0xFB39, // YOD
-    0xFB3A, // FINAL KAF
-    0xFB3B, // KAF
-    0xFB3C, // LAMED
-    0, // FINAL MEM
-    0xFB3E, // MEM
-    0, // FINAL NUN
-    0xFB40, // NUN
-    0xFB41, // SAMEKH
-    0, // AYIN
-    0xFB43, // FINAL PE
-    0xFB44, // PE
-    0, // FINAL TSADI
-    0xFB46, // TSADI
-    0xFB47, // QOF
-    0xFB48, // RESH
-    0xFB49, // SHIN
-    0xFB4A // TAV
-  };
-
-  hb_bool_t found = unicode->compose (a, b, ab);
-
-  if (!found && (b & ~0x7F) == 0x0580) {
-      // special-case Hebrew presentation forms that are excluded from
-      // standard normalization, but wanted for old fonts
-      switch (b) {
-      case 0x05B4: // HIRIQ
-         if (a == 0x05D9) { // YOD
-             *ab = 0xFB1D;
-             found = true;
-         }
-         break;
-      case 0x05B7: // patah
-         if (a == 0x05F2) { // YIDDISH YOD YOD
-             *ab = 0xFB1F;
-             found = true;
-         } else if (a == 0x05D0) { // ALEF
-             *ab = 0xFB2E;
-             found = true;
-         }
-         break;
-      case 0x05B8: // QAMATS
-         if (a == 0x05D0) { // ALEF
-             *ab = 0xFB2F;
-             found = true;
-         }
-         break;
-      case 0x05B9: // HOLAM
-         if (a == 0x05D5) { // VAV
-             *ab = 0xFB4B;
-             found = true;
-         }
-         break;
-      case 0x05BC: // DAGESH
-         if (a >= 0x05D0 && a <= 0x05EA) {
-             *ab = sDageshForms[a - 0x05D0];
-             found = (*ab != 0);
-         } else if (a == 0xFB2A) { // SHIN WITH SHIN DOT
-             *ab = 0xFB2C;
-             found = true;
-         } else if (a == 0xFB2B) { // SHIN WITH SIN DOT
-             *ab = 0xFB2D;
-             found = true;
-         }
-         break;
-      case 0x05BF: // RAFE
-         switch (a) {
-         case 0x05D1: // BET
-             *ab = 0xFB4C;
-             found = true;
-             break;
-         case 0x05DB: // KAF
-             *ab = 0xFB4D;
-             found = true;
-             break;
-         case 0x05E4: // PE
-             *ab = 0xFB4E;
-             found = true;
-             break;
-         }
-         break;
-      case 0x05C1: // SHIN DOT
-         if (a == 0x05E9) { // SHIN
-             *ab = 0xFB2A;
-             found = true;
-         } else if (a == 0xFB49) { // SHIN WITH DAGESH
-             *ab = 0xFB2C;
-             found = true;
-         }
-         break;
-      case 0x05C2: // SIN DOT
-         if (a == 0x05E9) { // SHIN
-             *ab = 0xFB2B;
-             found = true;
-         } else if (a == 0xFB49) { // SHIN WITH DAGESH
-             *ab = 0xFB2D;
-             found = true;
-         }
-         break;
-      }
-  }
-
-  return found;
+  return (bool) c->unicode->compose (a, b, ab);
 }
 
-
 static inline void
 set_glyph (hb_glyph_info_t &info, hb_font_t *font)
 {
-  font->get_glyph (info.codepoint, 0, &info.glyph_index());
+  (void) font->get_nominal_glyph (info.codepoint, &info.glyph_index());
 }
 
 static inline void
 output_char (hb_buffer_t *buffer, hb_codepoint_t unichar, hb_codepoint_t glyph)
 {
   buffer->cur().glyph_index() = glyph;
-  buffer->output_glyph (unichar);
-  _hb_glyph_info_set_unicode_props (&buffer->prev(), buffer->unicode);
+  buffer->output_glyph (unichar); /* This is very confusing indeed. */
+  _hb_glyph_info_set_unicode_props (&buffer->prev(), buffer);
 }
 
 static inline void
@@ -288,15 +117,17 @@ skip_char (hb_buffer_t *buffer)
 
 /* Returns 0 if didn't decompose, number of resulting characters otherwise. */
 static inline unsigned int
-decompose (hb_font_t *font, hb_buffer_t *buffer, bool shortest, hb_codepoint_t ab)
+decompose (const hb_ot_shape_normalize_context_t *c, bool shortest, hb_codepoint_t ab)
 {
   hb_codepoint_t a, b, a_glyph, b_glyph;
+  hb_buffer_t * const buffer = c->buffer;
+  hb_font_t * const font = c->font;
 
-  if (!decompose_func (buffer->unicode, ab, &a, &b) ||
-      (b && !font->get_glyph (b, 0, &b_glyph)))
+  if (!c->decompose (c, ab, &a, &b) ||
+      (b && !font->get_nominal_glyph (b, &b_glyph)))
     return 0;
 
-  bool has_a = font->get_glyph (a, 0, &a_glyph);
+  bool has_a = (bool) font->get_nominal_glyph (a, &a_glyph);
   if (shortest && has_a) {
     /* Output a and b */
     output_char (buffer, a, a_glyph);
@@ -308,7 +139,7 @@ decompose (hb_font_t *font, hb_buffer_t *buffer, bool shortest, hb_codepoint_t a
   }
 
   unsigned int ret;
-  if ((ret = decompose (font, buffer, shortest, a))) {
+  if ((ret = decompose (c, shortest, a))) {
     if (b) {
       output_char (buffer, b, b_glyph);
       return ret + 1;
@@ -328,62 +159,86 @@ decompose (hb_font_t *font, hb_buffer_t *buffer, bool shortest, hb_codepoint_t a
   return 0;
 }
 
-/* Returns 0 if didn't decompose, number of resulting characters otherwise. */
-static inline bool
-decompose_compatibility (hb_font_t *font, hb_buffer_t *buffer, hb_codepoint_t u)
-{
-  unsigned int len, i;
-  hb_codepoint_t decomposed[HB_UNICODE_MAX_DECOMPOSITION_LEN];
-  hb_codepoint_t glyphs[HB_UNICODE_MAX_DECOMPOSITION_LEN];
-
-  len = buffer->unicode->decompose_compatibility (u, decomposed);
-  if (!len)
-    return 0;
-
-  for (i = 0; i < len; i++)
-    if (!font->get_glyph (decomposed[i], 0, &glyphs[i]))
-      return 0;
-
-  for (i = 0; i < len; i++)
-    output_char (buffer, decomposed[i], glyphs[i]);
-
-  return len;
-}
-
-/* Returns true if recomposition may be benefitial. */
-static inline bool
-decompose_current_character (hb_font_t *font, hb_buffer_t *buffer, bool shortest)
+static inline void
+decompose_current_character (const hb_ot_shape_normalize_context_t *c, bool shortest)
 {
+  hb_buffer_t * const buffer = c->buffer;
+  hb_codepoint_t u = buffer->cur().codepoint;
   hb_codepoint_t glyph;
-  unsigned int len = 1;
 
-  /* Kind of a cute waterfall here... */
-  if (shortest && font->get_glyph (buffer->cur().codepoint, 0, &glyph))
+  if (shortest && c->font->get_nominal_glyph (u, &glyph))
+  {
     next_char (buffer, glyph);
-  else if ((len = decompose (font, buffer, shortest, buffer->cur().codepoint)))
+    return;
+  }
+
+  if (decompose (c, shortest, u))
+  {
     skip_char (buffer);
-  else if (!shortest && font->get_glyph (buffer->cur().codepoint, 0, &glyph))
+    return;
+  }
+
+  if (!shortest && c->font->get_nominal_glyph (u, &glyph))
+  {
     next_char (buffer, glyph);
-  else if ((len = decompose_compatibility (font, buffer, buffer->cur().codepoint)))
-    skip_char (buffer);
-  else
-    next_char (buffer, glyph); /* glyph is initialized in earlier branches. */
+    return;
+  }
+
+  if (_hb_glyph_info_is_unicode_space (&buffer->cur()))
+  {
+    hb_codepoint_t space_glyph;
+    hb_unicode_funcs_t::space_t space_type = buffer->unicode->space_fallback_type (u);
+    if (space_type != hb_unicode_funcs_t::NOT_SPACE && c->font->get_nominal_glyph (0x0020u, &space_glyph))
+    {
+      _hb_glyph_info_set_unicode_space_fallback_type (&buffer->cur(), space_type);
+      next_char (buffer, space_glyph);
+      buffer->scratch_flags |= HB_BUFFER_SCRATCH_FLAG_HAS_SPACE_FALLBACK;
+      return;
+    }
+  }
+
+  if (u == 0x2011u)
+  {
+    /* U+2011 is the only sensible character that is a no-break version of another character
+     * and not a space.  The space ones are handled already.  Handle this lone one. */
+    hb_codepoint_t other_glyph;
+    if (c->font->get_nominal_glyph (0x2010u, &other_glyph))
+    {
+      next_char (buffer, other_glyph);
+      return;
+    }
+  }
 
-  /*
-   * A recomposition would only be useful if we decomposed into at least three
-   * characters...
-   */
-  return len > 2;
+  next_char (buffer, glyph); /* glyph is initialized in earlier branches. */
 }
 
 static inline void
-handle_variation_selector_cluster (hb_font_t *font, hb_buffer_t *buffer, unsigned int end)
+handle_variation_selector_cluster (const hb_ot_shape_normalize_context_t *c, unsigned int end, bool short_circuit)
 {
-  for (; buffer->idx < end - 1;) {
+  /* TODO Currently if there's a variation-selector we give-up, it's just too hard. */
+  hb_buffer_t * const buffer = c->buffer;
+  hb_font_t * const font = c->font;
+  for (; buffer->idx < end - 1 && !buffer->in_error;) {
     if (unlikely (buffer->unicode->is_variation_selector (buffer->cur(+1).codepoint))) {
       /* The next two lines are some ugly lines... But work. */
-      font->get_glyph (buffer->cur().codepoint, buffer->cur(+1).codepoint, &buffer->cur().glyph_index());
-      buffer->replace_glyphs (2, 1, &buffer->cur().codepoint);
+      if (font->get_variation_glyph (buffer->cur().codepoint, buffer->cur(+1).codepoint, &buffer->cur().glyph_index()))
+      {
+       buffer->replace_glyphs (2, 1, &buffer->cur().codepoint);
+      }
+      else
+      {
+        /* Just pass on the two characters separately, let GSUB do its magic. */
+       set_glyph (buffer->cur(), font);
+       buffer->next_glyph ();
+       set_glyph (buffer->cur(), font);
+       buffer->next_glyph ();
+      }
+      /* Skip any further variation selectors. */
+      while (buffer->idx < end && unlikely (buffer->unicode->is_variation_selector (buffer->cur().codepoint)))
+      {
+       set_glyph (buffer->cur(), font);
+       buffer->next_glyph ();
+      }
     } else {
       set_glyph (buffer->cur(), font);
       buffer->next_glyph ();
@@ -395,31 +250,27 @@ handle_variation_selector_cluster (hb_font_t *font, hb_buffer_t *buffer, unsigne
   }
 }
 
-/* Returns true if recomposition may be benefitial. */
-static inline bool
-decompose_multi_char_cluster (hb_font_t *font, hb_buffer_t *buffer, unsigned int end)
+static inline void
+decompose_multi_char_cluster (const hb_ot_shape_normalize_context_t *c, unsigned int end, bool short_circuit)
 {
-  /* TODO Currently if there's a variation-selector we give-up, it's just too hard. */
-  for (unsigned int i = buffer->idx; i < end; i++)
+  hb_buffer_t * const buffer = c->buffer;
+  for (unsigned int i = buffer->idx; i < end && !buffer->in_error; i++)
     if (unlikely (buffer->unicode->is_variation_selector (buffer->info[i].codepoint))) {
-      handle_variation_selector_cluster (font, buffer, end);
-      return false;
+      handle_variation_selector_cluster (c, end, short_circuit);
+      return;
     }
 
-  while (buffer->idx < end)
-    decompose_current_character (font, buffer, false);
-  /* We can be smarter here and only return true if there are at least two ccc!=0 marks.
-   * But does not matter. */
-  return true;
+  while (buffer->idx < end && !buffer->in_error)
+    decompose_current_character (c, short_circuit);
 }
 
-static inline bool
-decompose_cluster (hb_font_t *font, hb_buffer_t *buffer, bool recompose, unsigned int end)
+static inline void
+decompose_cluster (const hb_ot_shape_normalize_context_t *c, unsigned int end, bool might_short_circuit, bool always_short_circuit)
 {
-  if (likely (buffer->idx + 1 == end))
-    return decompose_current_character (font, buffer, recompose);
+  if (likely (c->buffer->idx + 1 == end))
+    decompose_current_character (c, might_short_circuit);
   else
-    return decompose_multi_char_cluster (font, buffer, end);
+    decompose_multi_char_cluster (c, end, always_short_circuit);
 }
 
 
@@ -434,11 +285,28 @@ compare_combining_class (const hb_glyph_info_t *pa, const hb_glyph_info_t *pb)
 
 
 void
-_hb_ot_shape_normalize (hb_font_t *font, hb_buffer_t *buffer,
-                       hb_ot_shape_normalization_mode_t mode)
+_hb_ot_shape_normalize (const hb_ot_shape_plan_t *plan,
+                       hb_buffer_t *buffer,
+                       hb_font_t *font)
 {
-  bool recompose = mode != HB_OT_SHAPE_NORMALIZATION_MODE_DECOMPOSED;
-  bool can_use_recompose = false;
+  if (unlikely (!buffer->len)) return;
+
+  _hb_buffer_assert_unicode_vars (buffer);
+
+  hb_ot_shape_normalization_mode_t mode = plan->shaper->normalization_preference;
+  const hb_ot_shape_normalize_context_t c = {
+    plan,
+    buffer,
+    font,
+    buffer->unicode,
+    plan->shaper->decompose ? plan->shaper->decompose : decompose_unicode,
+    plan->shaper->compose   ? plan->shaper->compose   : compose_unicode
+  };
+
+  bool always_short_circuit = mode == HB_OT_SHAPE_NORMALIZATION_MODE_NONE;
+  bool might_short_circuit = always_short_circuit ||
+                            (mode != HB_OT_SHAPE_NORMALIZATION_MODE_DECOMPOSED &&
+                             mode != HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS_NO_SHORT_CIRCUIT);
   unsigned int count;
 
   /* We do a fairly straightforward yet custom normalization process in three
@@ -452,22 +320,18 @@ _hb_ot_shape_normalize (hb_font_t *font, hb_buffer_t *buffer,
 
   buffer->clear_output ();
   count = buffer->len;
-  for (buffer->idx = 0; buffer->idx < count;)
+  for (buffer->idx = 0; buffer->idx < count && !buffer->in_error;)
   {
     unsigned int end;
     for (end = buffer->idx + 1; end < count; end++)
-      if (buffer->cur().cluster != buffer->info[end].cluster)
+      if (likely (!HB_UNICODE_GENERAL_CATEGORY_IS_MARK (_hb_glyph_info_get_general_category (&buffer->info[end]))))
         break;
 
-    can_use_recompose = decompose_cluster (font, buffer, recompose, end) || can_use_recompose;
+    decompose_cluster (&c, end, might_short_circuit, always_short_circuit);
   }
   buffer->swap_buffers ();
 
 
-  if (mode != HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_FULL && !can_use_recompose)
-    return; /* Done! */
-
-
   /* Second round, reorder (inplace) */
 
   count = buffer->len;
@@ -481,21 +345,20 @@ _hb_ot_shape_normalize (hb_font_t *font, hb_buffer_t *buffer,
       if (_hb_glyph_info_get_modified_combining_class (&buffer->info[end]) == 0)
         break;
 
-    /* We are going to do a bubble-sort.  Only do this if the
-     * sequence is short.  Doing it on long sequences can result
-     * in an O(n^2) DoS. */
+    /* We are going to do a O(n^2).  Only do this if the sequence is short. */
     if (end - i > 10) {
       i = end;
       continue;
     }
 
-    hb_bubble_sort (buffer->info + i, end - i, compare_combining_class);
+    buffer->sort (i, end, compare_combining_class);
 
     i = end;
   }
 
 
-  if (!recompose)
+  if (mode == HB_OT_SHAPE_NORMALIZATION_MODE_NONE ||
+      mode == HB_OT_SHAPE_NORMALIZATION_MODE_DECOMPOSED)
     return;
 
   /* Third round, recompose */
@@ -507,24 +370,25 @@ _hb_ot_shape_normalize (hb_font_t *font, hb_buffer_t *buffer,
   count = buffer->len;
   unsigned int starter = 0;
   buffer->next_glyph ();
-  while (buffer->idx < count)
+  while (buffer->idx < count && !buffer->in_error)
   {
     hb_codepoint_t composed, glyph;
-    if (/* If mode is NOT COMPOSED_FULL (ie. it's COMPOSED_DIACRITICS), we don't try to
-        * compose a CCC=0 character with it's preceding starter. */
-       (mode == HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_FULL ||
-        _hb_glyph_info_get_modified_combining_class (&buffer->cur()) != 0) &&
+    if (/* We don't try to compose a non-mark character with it's preceding starter.
+        * This is both an optimization to avoid trying to compose every two neighboring
+        * glyphs in most scripts AND a desired feature for Hangul.  Apparently Hangul
+        * fonts are not designed to mix-and-match pre-composed syllables and Jamo. */
+       HB_UNICODE_GENERAL_CATEGORY_IS_MARK (_hb_glyph_info_get_general_category (&buffer->cur())) &&
        /* If there's anything between the starter and this char, they should have CCC
         * smaller than this character's. */
        (starter == buffer->out_len - 1 ||
         _hb_glyph_info_get_modified_combining_class (&buffer->prev()) < _hb_glyph_info_get_modified_combining_class (&buffer->cur())) &&
        /* And compose. */
-       compose_func (buffer->unicode,
-                     buffer->out_info[starter].codepoint,
-                     buffer->cur().codepoint,
-                     &composed) &&
+       c.compose (&c,
+                  buffer->out_info[starter].codepoint,
+                  buffer->cur().codepoint,
+                  &composed) &&
        /* And the font has glyph for the composite. */
-       font->get_glyph (composed, 0, &glyph))
+       font->get_nominal_glyph (composed, &glyph))
     {
       /* Composes. */
       buffer->next_glyph (); /* Copy to out-buffer. */
@@ -532,9 +396,10 @@ _hb_ot_shape_normalize (hb_font_t *font, hb_buffer_t *buffer,
         return;
       buffer->merge_out_clusters (starter, buffer->out_len);
       buffer->out_len--; /* Remove the second composable. */
-      buffer->out_info[starter].codepoint = composed; /* Modify starter and carry on. */
-      set_glyph (buffer->out_info[starter], font);
-      _hb_glyph_info_set_unicode_props (&buffer->out_info[starter], buffer->unicode);
+      /* Modify starter and carry on. */
+      buffer->out_info[starter].codepoint = composed;
+      buffer->out_info[starter].glyph_index() = glyph;
+      _hb_glyph_info_set_unicode_props (&buffer->out_info[starter], buffer);
 
       continue;
     }