Implement more granular cluster-merging

TODO: Documentation.

Fixes: https://bugs.freedesktop.org/show_bug.cgi?id=71445
diff --git a/src/hb-buffer-private.hh b/src/hb-buffer-private.hh
index 32d5a5f..3a2db48 100644
--- a/src/hb-buffer-private.hh
+++ b/src/hb-buffer-private.hh
@@ -50,6 +50,7 @@
   /* Information about how the text in the buffer should be treated */
   hb_unicode_funcs_t *unicode; /* Unicode functions */
   hb_buffer_flags_t flags; /* BOT / EOT / etc. */
+  hb_buffer_cluster_level_t cluster_level;
   hb_codepoint_t replacement; /* U+FFFD or something else. */
 
   /* Buffer contents */
diff --git a/src/hb-buffer.cc b/src/hb-buffer.cc
index 0a11fec..03fe8e1 100644
--- a/src/hb-buffer.cc
+++ b/src/hb-buffer.cc
@@ -507,9 +507,8 @@
 hb_buffer_t::merge_clusters_impl (unsigned int start,
 				  unsigned int end)
 {
-#ifdef HB_NO_MERGE_CLUSTERS
-  return;
-#endif
+  if (cluster_level == HB_BUFFER_CLUSTER_LEVEL_CHARACTERS)
+    return;
 
   unsigned int cluster = info[start].cluster;
 
@@ -536,9 +535,8 @@
 hb_buffer_t::merge_out_clusters (unsigned int start,
 				 unsigned int end)
 {
-#ifdef HB_NO_MERGE_CLUSTERS
-  return;
-#endif
+  if (cluster_level == HB_BUFFER_CLUSTER_LEVEL_CHARACTERS)
+    return;
 
   if (unlikely (end - start < 2))
     return;
@@ -738,6 +736,7 @@
 
     const_cast<hb_unicode_funcs_t *> (&_hb_unicode_funcs_nil),
     HB_BUFFER_FLAG_DEFAULT,
+    HB_BUFFER_CLUSTER_LEVEL_DEFAULT,
     HB_BUFFER_REPLACEMENT_CODEPOINT_DEFAULT,
 
     HB_BUFFER_CONTENT_TYPE_INVALID,
@@ -1083,6 +1082,41 @@
   return buffer->flags;
 }
 
+/**
+ * hb_buffer_set_cluster_level:
+ * @buffer: a buffer.
+ * @cluster_level: 
+ *
+ * 
+ *
+ * Since: 0.9.42
+ **/
+void
+hb_buffer_set_cluster_level (hb_buffer_t       *buffer,
+		     hb_buffer_cluster_level_t  cluster_level)
+{
+  if (unlikely (hb_object_is_inert (buffer)))
+    return;
+
+  buffer->cluster_level = cluster_level;
+}
+
+/**
+ * hb_buffer_get_cluster_level:
+ * @buffer: a buffer.
+ *
+ * 
+ *
+ * Return value: 
+ *
+ * Since: 0.9.42
+ **/
+hb_buffer_cluster_level_t
+hb_buffer_get_cluster_level (hb_buffer_t *buffer)
+{
+  return buffer->cluster_level;
+}
+
 
 /**
  * hb_buffer_set_replacement_codepoint:
diff --git a/src/hb-buffer.h b/src/hb-buffer.h
index 520141b..4b285bb 100644
--- a/src/hb-buffer.h
+++ b/src/hb-buffer.h
@@ -185,7 +185,19 @@
 hb_buffer_flags_t
 hb_buffer_get_flags (hb_buffer_t *buffer);
 
+typedef enum {
+  HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES	= 0,
+  HB_BUFFER_CLUSTER_LEVEL_MONOTONE_CHARACTERS	= 1,
+  HB_BUFFER_CLUSTER_LEVEL_CHARACTERS		= 2,
+  HB_BUFFER_CLUSTER_LEVEL_DEFAULT = HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES
+} hb_buffer_cluster_level_t;
 
+void
+hb_buffer_set_cluster_level (hb_buffer_t               *buffer,
+			     hb_buffer_cluster_level_t  cluster_level);
+
+hb_buffer_cluster_level_t
+hb_buffer_get_cluster_level (hb_buffer_t *buffer);
 
 #define HB_BUFFER_REPLACEMENT_CODEPOINT_DEFAULT 0xFFFDu
 
diff --git a/src/hb-ot-shape-complex-hangul.cc b/src/hb-ot-shape-complex-hangul.cc
index 6ac18b0..763dbf0 100644
--- a/src/hb-ot-shape-complex-hangul.cc
+++ b/src/hb-ot-shape-complex-hangul.cc
@@ -209,13 +209,8 @@
 	  hb_glyph_info_t tone = info[end];
 	  memmove (&info[start + 1], &info[start], (end - start) * sizeof (hb_glyph_info_t));
 	  info[start] = tone;
+	  buffer->merge_out_clusters (start, end + 1);
 	}
-	/* Merge clusters across the (possibly reordered) syllable+tone.
-	 * We want to merge even in the zero-width tone mark case here,
-	 * so that clustering behavior isn't dependent on how the tone mark
-	 * is handled by the font.
-	 */
-	buffer->merge_out_clusters (start, end + 1);
       }
       else
       {
@@ -296,7 +291,8 @@
 	}
 	else
 	  end = start + 2;
-	buffer->merge_out_clusters (start, end);
+	if (buffer->cluster_level == HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES)
+	  buffer->merge_out_clusters (start, end);
 	continue;
       }
     }
@@ -368,7 +364,8 @@
 	  info[i++].hangul_shaping_feature() = VJMO;
 	  if (i < end)
 	    info[i++].hangul_shaping_feature() = TJMO;
-	  buffer->merge_out_clusters (start, end);
+	  if (buffer->cluster_level == HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES)
+	    buffer->merge_out_clusters (start, end);
 	  continue;
 	}
       }
diff --git a/src/hb-ot-shape.cc b/src/hb-ot-shape.cc
index 5ead0b7..d290cad 100644
--- a/src/hb-ot-shape.cc
+++ b/src/hb-ot-shape.cc
@@ -264,6 +264,9 @@
 static void
 hb_form_clusters (hb_buffer_t *buffer)
 {
+  if (buffer->cluster_level != HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES)
+    return;
+
   /* Loop duplicated in hb_ensure_native_direction(). */
   unsigned int base = 0;
   unsigned int count = buffer->len;
@@ -301,10 +304,14 @@
       if (likely (!HB_UNICODE_GENERAL_CATEGORY_IS_MARK (_hb_glyph_info_get_general_category (&info[i]))))
       {
 	buffer->reverse_range (base, i);
+	if (buffer->cluster_level == HB_BUFFER_CLUSTER_LEVEL_MONOTONE_CHARACTERS)
+	  buffer->merge_clusters (base, i);
 	base = i;
       }
     }
     buffer->reverse_range (base, count);
+    if (buffer->cluster_level == HB_BUFFER_CLUSTER_LEVEL_MONOTONE_CHARACTERS)
+      buffer->merge_clusters (base, count);
 
     buffer->reverse ();