Add hb_buffer_normalize_glyphs() and hb-shape --normalize-glyphs

This reorders glyphs within the cluster to a nominal order.  This should
have no visible effect on the output, but helps with testing, for
getting the same hb-shape output for visually-equal glyphs for each
cluster.
diff --git a/src/hb-buffer.cc b/src/hb-buffer.cc
index 44571da..ec29659 100644
--- a/src/hb-buffer.cc
+++ b/src/hb-buffer.cc
@@ -887,3 +887,79 @@
 }
 
 
+static int
+compare_info_codepoint (const hb_glyph_info_t *pa,
+			const hb_glyph_info_t *pb)
+{
+  return (int) pb->codepoint - (int) pa->codepoint;
+}
+
+static inline void
+normalize_glyphs_cluster (hb_buffer_t *buffer,
+			  unsigned int start,
+			  unsigned int end,
+			  bool backward)
+{
+  hb_glyph_position_t *pos = buffer->pos;
+
+  /* Total cluster advance */
+  hb_position_t total_x_advance = 0, total_y_advance = 0;
+  for (unsigned int i = start; i < end; i++)
+  {
+    total_x_advance += pos[i].x_advance;
+    total_y_advance += pos[i].y_advance;
+  }
+
+  hb_position_t x_advance = 0, y_advance = 0;
+  for (unsigned int i = start; i < end; i++)
+  {
+    pos[i].x_offset += x_advance;
+    pos[i].y_offset += y_advance;
+
+    x_advance += pos[i].x_advance;
+    y_advance += pos[i].y_advance;
+
+    pos[i].x_advance = 0;
+    pos[i].y_advance = 0;
+  }
+
+  if (backward)
+  {
+    /* Transfer all cluster advance to the last glyph. */
+    pos[end - 1].x_advance = total_x_advance;
+    pos[end - 1].y_advance = total_y_advance;
+
+    hb_bubble_sort (buffer->info + start, end - start - 1, compare_info_codepoint, buffer->pos + start);
+  } else {
+    /* Transfer all cluster advance to the first glyph. */
+    pos[start].x_advance += total_x_advance;
+    pos[start].y_advance += total_y_advance;
+    for (unsigned int i = start + 1; i < end; i++) {
+      pos[i].x_offset -= total_x_advance;
+      pos[i].y_offset -= total_y_advance;
+    }
+    hb_bubble_sort (buffer->info + start + 1, end - start - 1, compare_info_codepoint, buffer->pos + start + 1);
+  }
+}
+
+void
+hb_buffer_normalize_glyphs (hb_buffer_t *buffer)
+{
+  assert (buffer->have_positions);
+  /* XXX assert (buffer->have_glyphs); */
+
+  bool backward = HB_DIRECTION_IS_BACKWARD (buffer->props.direction);
+
+  unsigned int count = buffer->len;
+  if (unlikely (!count)) return;
+  hb_glyph_info_t *info = buffer->info;
+
+  unsigned int start = 0;
+  unsigned int end;
+  for (end = start + 1; end < count; end++)
+    if (info[start].cluster != info[end].cluster) {
+      normalize_glyphs_cluster (buffer, start, end, backward);
+      start = end;
+    }
+  normalize_glyphs_cluster (buffer, start, end, backward);
+}
diff --git a/src/hb-buffer.h b/src/hb-buffer.h
index 73adc2e..d67cfd6 100644
--- a/src/hb-buffer.h
+++ b/src/hb-buffer.h
@@ -193,6 +193,19 @@
                                unsigned int *length);
 
 
+/* Reorders a glyph buffer to have canonical in-cluster glyph order / position.
+ * The resulting clusters should behave identical to pre-reordering clusters.
+ * NOTE: This has nothing to do with Unicode normalization. */
+void
+hb_buffer_normalize_glyphs (hb_buffer_t *buffer);
+
+/*
+ * NOT IMPLEMENTED
+void
+hb_buffer_normalize_characters (hb_buffer_t *buffer);
+*/
+
+
 HB_END_DECLS
 
 #endif /* HB_BUFFER_H */
diff --git a/src/hb-private.hh b/src/hb-private.hh
index 29cd68c..02ed9ce 100644
--- a/src/hb-private.hh
+++ b/src/hb-private.hh
@@ -737,8 +737,8 @@
 #define FLAG(x) (1<<(x))
 
 
-template <typename T> inline void
-hb_bubble_sort (T *array, unsigned int len, int(*compar)(const T *, const T *))
+template <typename T, typename T2> inline void
+hb_bubble_sort (T *array, unsigned int len, int(*compar)(const T *, const T *), T2 *array2)
 {
   if (unlikely (!len))
     return;
@@ -748,11 +748,21 @@
     unsigned int new_k = 0;
 
     for (unsigned int j = 0; j < k; j++)
-      if (compar (&array[j], &array[j+1]) > 0) {
-        T t;
-	t = array[j];
-	array[j] = array[j + 1];
-	array[j + 1] = t;
+      if (compar (&array[j], &array[j+1]) > 0)
+      {
+        {
+	  T t;
+	  t = array[j];
+	  array[j] = array[j + 1];
+	  array[j + 1] = t;
+	}
+        if (array2)
+        {
+	  T2 t;
+	  t = array2[j];
+	  array2[j] = array2[j + 1];
+	  array2[j + 1] = t;
+	}
 
 	new_k = j;
       }
@@ -760,6 +770,11 @@
   } while (k);
 }
 
+template <typename T> inline void
+hb_bubble_sort (T *array, unsigned int len, int(*compar)(const T *, const T *))
+{
+  hb_bubble_sort (array, len, compar, (int *) NULL);
+}
 
 
 
diff --git a/util/options.cc b/util/options.cc
index db1b244..584190e 100644
--- a/util/options.cc
+++ b/util/options.cc
@@ -396,6 +396,7 @@
     {"language",	0, 0, G_OPTION_ARG_STRING,	&this->language,		"Set text language (default: $LANG)",	"langstr"},
     {"script",		0, 0, G_OPTION_ARG_STRING,	&this->script,			"Set text script (default: auto)",	"ISO-15924 tag"},
     {"utf8-clusters",	0, 0, G_OPTION_ARG_NONE,	&this->utf8_clusters,		"Use UTF8 byte indices, not char indices",	NULL},
+    {"normalize-glyphs",0, 0, G_OPTION_ARG_NONE,	&this->normalize_glyphs,	"Rearrange glyph clusters in nominal order",	NULL},
     {NULL}
   };
   parser->add_group (entries,
diff --git a/util/options.hh b/util/options.hh
index 9b7baa7..2485230 100644
--- a/util/options.hh
+++ b/util/options.hh
@@ -148,6 +148,7 @@
     num_features = 0;
     shapers = NULL;
     utf8_clusters = false;
+    normalize_glyphs = false;
 
     add_options (parser);
   }
@@ -188,7 +189,10 @@
 
   hb_bool_t shape (hb_font_t *font, hb_buffer_t *buffer)
   {
-    return hb_shape_full (font, buffer, features, num_features, shapers);
+    hb_bool_t res = hb_shape_full (font, buffer, features, num_features, shapers);
+    if (normalize_glyphs)
+      hb_buffer_normalize_glyphs (buffer);
+    return res;
   }
 
   void shape_closure (const char *text, int text_len,
@@ -208,6 +212,7 @@
   unsigned int num_features;
   char **shapers;
   hb_bool_t utf8_clusters;
+  hb_bool_t normalize_glyphs;
 };