[HarfBuzz] harfbuzz-ng: Branch 'master' - 12 commits

Behdad Esfahbod behdad at kemper.freedesktop.org
Tue Jul 31 18:37:02 PDT 2012


 src/hb-buffer-private.hh             |    3 
 src/hb-buffer.cc                     |   24 ++--
 src/hb-glib.cc                       |   30 +++++
 src/hb-icu.cc                        |   36 ++++++-
 src/hb-ot-layout-gpos-table.hh       |   15 +-
 src/hb-ot-layout-gsub-table.hh       |   24 ++--
 src/hb-ot-layout-gsubgpos-private.hh |  106 ++++++++++++++++++--
 src/hb-ot-layout-private.hh          |   16 ---
 src/hb-ot-layout.cc                  |  144 +++-------------------------
 src/hb-ot-layout.h                   |   12 +-
 src/hb-ot-shape-complex-arabic.cc    |   37 +++----
 src/hb-ot-shape-complex-indic.cc     |   54 ++++++----
 src/hb-ot-shape-complex-misc.cc      |   67 +++++--------
 src/hb-ot-shape-complex-private.hh   |  179 ++++++++++-------------------------
 src/hb-ot-shape-normalize-private.hh |    4 
 src/hb-ot-shape-normalize.cc         |   53 ++++++----
 src/hb-ot-shape-private.hh           |    2 
 src/hb-ot-shape.cc                   |   26 ++---
 src/hb-unicode-private.hh            |    1 
 src/hb-unicode.cc                    |   27 +++++
 src/hb-unicode.h                     |   37 +++++++
 test/api/hb-test.h                   |    1 
 test/api/test-unicode.c              |   50 +++++++++
 23 files changed, 519 insertions(+), 429 deletions(-)

New commits:
commit 378d279bbf692195c4654e312dae854ab3be04cf
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Tue Jul 31 21:36:16 2012 -0400

    Implement Unicode compatibility decompositions
    
    Based on patch from Philip Withnall.
    https://bugs.freedesktop.org/show_bug.cgi?id=41095

diff --git a/src/hb-glib.cc b/src/hb-glib.cc
index 6b655dd..5246363 100644
--- a/src/hb-glib.cc
+++ b/src/hb-glib.cc
@@ -336,6 +336,36 @@ hb_glib_unicode_decompose (hb_unicode_funcs_t *ufuncs HB_UNUSED,
   return ret;
 }
 
+static unsigned int
+hb_glib_unicode_decompose_compatibility (hb_unicode_funcs_t *ufuncs,
+					 hb_codepoint_t      u,
+					 hb_codepoint_t     *decomposed,
+					 void               *user_data HB_UNUSED)
+{
+#if GLIB_CHECK_VERSION(2,29,12)
+  return g_unichar_fully_decompose (u, TRUE, decomposed, HB_UNICODE_MAX_DECOMPOSITION_LEN);
+#endif
+
+  /* If the user doesn't have GLib >= 2.29.12 we have to perform
+   * a round trip to UTF-8 and the associated memory management dance. */
+  gchar utf8[6];
+  gchar *utf8_decomposed, *c;
+  gsize utf8_len, utf8_decomposed_len, i;
+
+  /* Convert @u to UTF-8 and normalise it in NFKD mode. This performs the compatibility decomposition. */
+  utf8_len = g_unichar_to_utf8 (u, utf8);
+  utf8_decomposed = g_utf8_normalize (utf8, utf8_len, G_NORMALIZE_NFKD);
+  utf8_decomposed_len = g_utf8_strlen (utf8_decomposed, -1);
+
+  assert (utf8_decomposed_len <= HB_UNICODE_MAX_DECOMPOSITION_LEN);
+
+  for (i = 0, c = utf8_decomposed; i < utf8_decomposed_len; i++, c = g_utf8_next_char (c))
+    *decomposed++ = g_utf8_get_char (c);
+
+  g_free (utf8_decomposed);
+
+  return utf8_decomposed_len;
+}
 
 extern HB_INTERNAL const hb_unicode_funcs_t _hb_glib_unicode_funcs;
 const hb_unicode_funcs_t _hb_glib_unicode_funcs = {
diff --git a/src/hb-icu.cc b/src/hb-icu.cc
index 491c1c8..dce6103 100644
--- a/src/hb-icu.cc
+++ b/src/hb-icu.cc
@@ -207,7 +207,7 @@ hb_icu_unicode_decompose (hb_unicode_funcs_t *ufuncs HB_UNUSED,
 			  hb_codepoint_t     *b,
 			  void               *user_data HB_UNUSED)
 {
-  UChar utf16[2], normalized[20];
+  UChar utf16[2], normalized[2 * HB_UNICODE_MAX_DECOMPOSITION_LEN + 1];
   int len;
   hb_bool_t ret, err;
   UErrorCode icu_err;
@@ -271,6 +271,40 @@ hb_icu_unicode_decompose (hb_unicode_funcs_t *ufuncs HB_UNUSED,
   return ret;
 }
 
+static unsigned int
+hb_icu_unicode_decompose_compatibility (hb_unicode_funcs_t *ufuncs HB_UNUSED,
+					hb_codepoint_t      u,
+					hb_codepoint_t     *decomposed,
+					void               *user_data HB_UNUSED)
+{
+  UChar utf16[2], normalized[2 * HB_UNICODE_MAX_DECOMPOSITION_LEN + 1];
+  gint len;
+  int32_t utf32_len;
+  hb_bool_t err;
+  UErrorCode icu_err;
+
+  /* Copy @u into a UTF-16 array to be passed to ICU. */
+  len = 0;
+  err = FALSE;
+  U16_APPEND (utf16, len, ARRAY_LENGTH (utf16), u, err);
+  if (err)
+    return 0;
+
+  /* Normalise the codepoint using NFKD mode. */
+  icu_err = U_ZERO_ERROR;
+  len = unorm_normalize (utf16, len, UNORM_NFKD, 0, normalized, ARRAY_LENGTH (normalized), &icu_err);
+  if (icu_err)
+    return 0;
+
+  /* Convert the decomposed form from UTF-16 to UTF-32. */
+  icu_err = U_ZERO_ERROR;
+  u_strToUTF32 ((UChar32*) decomposed, HB_UNICODE_MAX_DECOMPOSITION_LEN, &utf32_len, normalized, len, &icu_err);
+  if (icu_err)
+    return 0;
+
+  return utf32_len;
+}
+
 
 extern HB_INTERNAL const hb_unicode_funcs_t _hb_icu_unicode_funcs;
 const hb_unicode_funcs_t _hb_icu_unicode_funcs = {
diff --git a/src/hb-ot-shape-normalize.cc b/src/hb-ot-shape-normalize.cc
index d4b0b27..46c89ec 100644
--- a/src/hb-ot-shape-normalize.cc
+++ b/src/hb-ot-shape-normalize.cc
@@ -62,7 +62,8 @@
  *     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.
+ *     well, use the decomposition (preferring the canonical decomposition, but
+ *     falling back to the compatibility decomposition if necessary).
  *
  *   - The Indic shaper requests decomposed output.  This will handle splitting
  *     matra for the Indic shaper.
@@ -111,29 +112,45 @@ decompose (hb_font_t *font, hb_buffer_t *buffer,
   return false;
 }
 
-static void
-decompose_current_glyph (hb_font_t *font, hb_buffer_t *buffer,
-			 bool shortest)
+static bool
+decompose_compatibility (hb_font_t *font, hb_buffer_t *buffer,
+			 hb_codepoint_t u)
 {
-  if (decompose (font, buffer, shortest, buffer->cur().codepoint))
-    buffer->skip_glyph ();
-  else
-    buffer->next_glyph ();
+  unsigned int len, i;
+  hb_codepoint_t decomposed[HB_UNICODE_MAX_DECOMPOSITION_LEN];
+
+  len = hb_unicode_decompose_compatibility (buffer->unicode, u, decomposed);
+  if (!len)
+    return false;
+
+  hb_codepoint_t glyph;
+  for (i = 0; i < len; i++)
+    if (!hb_font_get_glyph (font, decomposed[i], 0, &glyph))
+      return false;
+
+  for (i = 0; i < len; i++)
+    output_glyph (buffer, decomposed[i]);
+
+  return true;
 }
 
 static void
-decompose_single_char_cluster (hb_font_t *font, hb_buffer_t *buffer,
-			       bool will_recompose)
+decompose_current_character (hb_font_t *font, hb_buffer_t *buffer,
+			     bool shortest)
 {
   hb_codepoint_t glyph;
 
-  /* If recomposing and font supports this, we're good to go */
-  if (will_recompose && hb_font_get_glyph (font, buffer->cur().codepoint, 0, &glyph)) {
+  /* Kind of a cute waterfall here... */
+  if (shortest && hb_font_get_glyph (font, buffer->cur().codepoint, 0, &glyph))
+    buffer->next_glyph ();
+  else if (decompose (font, buffer, shortest, buffer->cur().codepoint))
+    buffer->skip_glyph ();
+  else if (!shortest && hb_font_get_glyph (font, buffer->cur().codepoint, 0, &glyph))
+    buffer->next_glyph ();
+  else if (decompose_compatibility (font, buffer, buffer->cur().codepoint))
+    buffer->skip_glyph ();
+  else
     buffer->next_glyph ();
-    return;
-  }
-
-  decompose_current_glyph (font, buffer, will_recompose);
 }
 
 static void
@@ -149,7 +166,7 @@ decompose_multi_char_cluster (hb_font_t *font, hb_buffer_t *buffer,
     }
 
   while (buffer->idx < end)
-    decompose_current_glyph (font, buffer, false);
+    decompose_current_character (font, buffer, false);
 }
 
 static int
@@ -188,7 +205,7 @@ _hb_ot_shape_normalize (hb_font_t *font, hb_buffer_t *buffer,
         break;
 
     if (buffer->idx + 1 == end)
-      decompose_single_char_cluster (font, buffer, recompose);
+      decompose_current_character (font, buffer, recompose);
     else {
       decompose_multi_char_cluster (font, buffer, end);
       has_multichar_clusters = true;
diff --git a/src/hb-unicode-private.hh b/src/hb-unicode-private.hh
index 1ce5adc..ba791eb 100644
--- a/src/hb-unicode-private.hh
+++ b/src/hb-unicode-private.hh
@@ -50,6 +50,7 @@
   HB_UNICODE_FUNC_IMPLEMENT (script) \
   HB_UNICODE_FUNC_IMPLEMENT (compose) \
   HB_UNICODE_FUNC_IMPLEMENT (decompose) \
+  HB_UNICODE_FUNC_IMPLEMENT (decompose_compatibility) \
   /* ^--- Add new callbacks here */
 
 /* Simple callbacks are those taking a hb_codepoint_t and returning a hb_codepoint_t */
diff --git a/src/hb-unicode.cc b/src/hb-unicode.cc
index b05b290..f300fed 100644
--- a/src/hb-unicode.cc
+++ b/src/hb-unicode.cc
@@ -99,6 +99,15 @@ hb_unicode_decompose_nil (hb_unicode_funcs_t *ufuncs    HB_UNUSED,
 }
 
 
+static unsigned int
+hb_unicode_decompose_compatibility_nil (hb_unicode_funcs_t *ufuncs     HB_UNUSED,
+					hb_codepoint_t      u          HB_UNUSED,
+					hb_codepoint_t     *decomposed HB_UNUSED,
+					void               *user_data  HB_UNUSED)
+{
+  return 0;
+}
+
 
 hb_unicode_funcs_t *
 hb_unicode_funcs_get_default (void)
@@ -312,6 +321,23 @@ hb_unicode_decompose (hb_unicode_funcs_t *ufuncs,
   return ufuncs->func.decompose (ufuncs, ab, a, b, ufuncs->user_data.decompose);
 }
 
+unsigned int
+hb_unicode_decompose_compatibility (hb_unicode_funcs_t *ufuncs,
+				    hb_codepoint_t      u,
+				    hb_codepoint_t     *decomposed)
+{
+  unsigned int ret = ufuncs->func.decompose_compatibility (ufuncs, u,
+							   decomposed,
+							   ufuncs->user_data.decompose_compatibility);
+  if (ret == 1 && u == decomposed[0]) {
+    decomposed[0] = 0;
+    return 0;
+  }
+
+  decomposed[ret] = 0;
+
+  return ret;
+}
 
 
 unsigned int
@@ -380,4 +406,3 @@ _hb_unicode_modified_combining_class (hb_unicode_funcs_t *ufuncs,
 
   return c;
 }
-
diff --git a/src/hb-unicode.h b/src/hb-unicode.h
index 808c6e1..2af2d67 100644
--- a/src/hb-unicode.h
+++ b/src/hb-unicode.h
@@ -1,7 +1,7 @@
 /*
  * Copyright © 2009  Red Hat, Inc.
  * Copyright © 2011  Codethink Limited
- * Copyright © 2011  Google, Inc.
+ * Copyright © 2011,2012  Google, Inc.
  *
  *  This is part of HarfBuzz, a text shaping library.
  *
@@ -122,6 +122,32 @@ typedef hb_bool_t			(*hb_unicode_decompose_func_t)		(hb_unicode_funcs_t *ufuncs,
 										 hb_codepoint_t     *b,
 										 void               *user_data);
 
+/**
+ * hb_unicode_decompose_compatibility_func_t:
+ * @ufuncs: Unicode function structure
+ * @u: codepoint to decompose
+ * @decomposed: address of codepoint array (of length %HB_UNICODE_MAX_DECOMPOSITION_LEN) to write decomposition into
+ * @user_data: user data pointer as passed to hb_unicode_funcs_set_decompose_compatibility_func()
+ *
+ * Fully decompose @u to its Unicode compatibility decomposition. The codepoints of the decomposition will be written to @decomposed.
+ * The complete length of the decomposition will be returned.
+ *
+ * If @u has no compatibility decomposition, zero should be returned.
+ *
+ * The Unicode standard guarantees that a buffer of length %HB_UNICODE_MAX_DECOMPOSITION_LEN codepoints will always be sufficient for any
+ * compatibility decomposition plus an terminating value of 0.  Consequently, @decompose must be allocated by the caller to be at least this length.  Implementations
+ * of this function type must ensure that they do not write past the provided array.
+ *
+ * Return value: number of codepoints in the full compatibility decomposition of @u, or 0 if no decomposition available.
+ */
+typedef unsigned int			(*hb_unicode_decompose_compatibility_func_t)	(hb_unicode_funcs_t *ufuncs,
+											 hb_codepoint_t      u,
+											 hb_codepoint_t     *decomposed,
+											 void               *user_data);
+
+/* See Unicode 6.1 for details on the maximum decomposition length. */
+#define HB_UNICODE_MAX_DECOMPOSITION_LEN (18+1) /* codepoints */
+
 /* setters */
 
 void
@@ -159,6 +185,10 @@ hb_unicode_funcs_set_decompose_func (hb_unicode_funcs_t *ufuncs,
 				     hb_unicode_decompose_func_t decompose_func,
 				     void *user_data, hb_destroy_func_t destroy);
 
+void
+hb_unicode_funcs_set_decompose_compatibility_func (hb_unicode_funcs_t *ufuncs,
+						   hb_unicode_decompose_compatibility_func_t decompose_compatibility_func,
+						   void *user_data, hb_destroy_func_t destroy);
 
 /* accessors */
 
@@ -193,6 +223,11 @@ hb_unicode_decompose (hb_unicode_funcs_t *ufuncs,
 		      hb_codepoint_t     *a,
 		      hb_codepoint_t     *b);
 
+unsigned int
+hb_unicode_decompose_compatibility (hb_unicode_funcs_t *ufuncs,
+				    hb_codepoint_t      u,
+				    hb_codepoint_t     *decomposed);
+
 HB_END_DECLS
 
 #endif /* HB_UNICODE_H */
diff --git a/test/api/hb-test.h b/test/api/hb-test.h
index d569757..8655f41 100644
--- a/test/api/hb-test.h
+++ b/test/api/hb-test.h
@@ -33,6 +33,7 @@
 
 #include <stdlib.h>
 #include <string.h>
+#include <stdio.h>
 
 HB_BEGIN_DECLS
 
diff --git a/test/api/test-unicode.c b/test/api/test-unicode.c
index a420bf3..96c61dd 100644
--- a/test/api/test-unicode.c
+++ b/test/api/test-unicode.c
@@ -786,6 +786,7 @@ test_unicode_normalization (gconstpointer user_data)
 {
   hb_unicode_funcs_t *uf = (hb_unicode_funcs_t *) user_data;
   gunichar a, b, ab;
+  hb_codepoint_t decomposed[HB_UNICODE_MAX_DECOMPOSITION_LEN];
 
 
   /* Test compose() */
@@ -849,6 +850,55 @@ test_unicode_normalization (gconstpointer user_data)
   g_assert (hb_unicode_decompose (uf, 0xCE31, &a, &b) && a == 0xCE20 && b == 0x11B8);
   g_assert (hb_unicode_decompose (uf, 0xCE20, &a, &b) && a == 0x110E && b == 0x1173);
 
+
+  /* Test decompose_compatibility() */
+
+  /* Not decomposable */
+  g_assert (hb_unicode_decompose_compatibility (uf, 0x0041, decomposed) == 0);
+  g_assert (hb_unicode_decompose_compatibility (uf, 0x1F632, decomposed) == 0);
+
+  /* Singletons */
+  g_assert (hb_unicode_decompose_compatibility (uf, 0x00B5, decomposed) == 1 && decomposed[0] == 0x03BC);
+  g_assert (hb_unicode_decompose_compatibility (uf, 0x03D6, decomposed) == 1 && decomposed[0] == 0x03C0);
+
+  /* Arabic compatibility */
+  g_assert (hb_unicode_decompose_compatibility (uf, 0xFB54, decomposed) == 1 && decomposed[0] == 0x067B);
+
+  /* Longest decomposition ever */
+  g_assert (18 <= HB_UNICODE_MAX_DECOMPOSITION_LEN);
+  g_assert (hb_unicode_decompose_compatibility (uf, 0xFDFA, decomposed) == 18 && decomposed[17] == 0x0645);
+
+  /* Note: we deliberately don't test characters that have canonical decompositions but no
+   * compatibility decomposition against the decompose_compatibility() function as that we
+   * leave up to implementations (for now). */
+
+  /* Spaces */
+  g_assert (hb_unicode_decompose_compatibility (uf, 0x2002, decomposed) == 1 && decomposed[0] == 0x0020);
+  g_assert (hb_unicode_decompose_compatibility (uf, 0x2003, decomposed) == 1 && decomposed[0] == 0x0020);
+  g_assert (hb_unicode_decompose_compatibility (uf, 0x2004, decomposed) == 1 && decomposed[0] == 0x0020);
+  g_assert (hb_unicode_decompose_compatibility (uf, 0x2005, decomposed) == 1 && decomposed[0] == 0x0020);
+  g_assert (hb_unicode_decompose_compatibility (uf, 0x2006, decomposed) == 1 && decomposed[0] == 0x0020);
+  g_assert (hb_unicode_decompose_compatibility (uf, 0x2008, decomposed) == 1 && decomposed[0] == 0x0020);
+  g_assert (hb_unicode_decompose_compatibility (uf, 0x2009, decomposed) == 1 && decomposed[0] == 0x0020);
+  g_assert (hb_unicode_decompose_compatibility (uf, 0x200A, decomposed) == 1 && decomposed[0] == 0x0020);
+
+  /* Pairs */
+  g_assert (hb_unicode_decompose_compatibility (uf, 0x0587, decomposed) == 2 &&
+            decomposed[0] == 0x0565 && decomposed[1] == 0x0582);
+  g_assert (hb_unicode_decompose_compatibility (uf, 0x2017, decomposed) == 2 &&
+            decomposed[0] == 0x0020 && decomposed[1] == 0x0333);
+  g_assert (hb_unicode_decompose_compatibility (uf, 0x2025, decomposed) == 2 &&
+            decomposed[0] == 0x002E && decomposed[1] == 0x002E);
+  g_assert (hb_unicode_decompose_compatibility (uf, 0x2033, decomposed) == 2 &&
+            decomposed[0] == 0x2032 && decomposed[1] == 0x2032);
+
+  /* Triples */
+  g_assert (hb_unicode_decompose_compatibility (uf, 0x2026, decomposed) == 3 &&
+            decomposed[0] == 0x002E && decomposed[1] == 0x002E && decomposed[2] == 0x002E);
+  g_assert (hb_unicode_decompose_compatibility (uf, 0x2034, decomposed) == 3 &&
+            decomposed[0] == 0x2032 && decomposed[1] == 0x2032 && decomposed[2] == 0x2032);
+  g_assert (hb_unicode_decompose_compatibility (uf, 0x213B, decomposed) == 3 &&
+            decomposed[0] == 0x0046 && decomposed[1] == 0x0041 && decomposed[2] == 0x0058);
 }
 
 
commit 321ec29cc270e7e66a529696b70b2caac553c95f
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Tue Jul 31 21:10:16 2012 -0400

    Remove unused function

diff --git a/src/hb-ot-shape-complex-indic.cc b/src/hb-ot-shape-complex-indic.cc
index b384e0e..55eedab 100644
--- a/src/hb-ot-shape-complex-indic.cc
+++ b/src/hb-ot-shape-complex-indic.cc
@@ -428,13 +428,6 @@ override_features_indic (const hb_ot_complex_shaper_t  *shaper,
 }
 
 
-hb_ot_shape_normalization_mode_t
-_hb_ot_shape_complex_normalization_preference_indic (const hb_segment_properties_t *props)
-{
-  return HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS;
-}
-
-
 static void
 setup_masks_indic (const hb_ot_complex_shaper_t *shaper,
 		   const hb_ot_map_t            *map,
commit 69cc492dc120847ed00cae65ec958593ebf550c5
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Tue Jul 31 14:51:36 2012 -0400

    [buffer] Minor

diff --git a/src/hb-buffer-private.hh b/src/hb-buffer-private.hh
index d6189d2..c96723a 100644
--- a/src/hb-buffer-private.hh
+++ b/src/hb-buffer-private.hh
@@ -141,9 +141,11 @@ struct hb_buffer_t {
   HB_INTERNAL void swap_buffers (void);
   HB_INTERNAL void clear_output (void);
   HB_INTERNAL void clear_positions (void);
+
   HB_INTERNAL void replace_glyphs (unsigned int num_in,
 				   unsigned int num_out,
 				   const hb_codepoint_t *glyph_data);
+
   HB_INTERNAL void replace_glyph (hb_codepoint_t glyph_index);
   /* Makes a copy of the glyph at idx to output and replace glyph_index */
   HB_INTERNAL void output_glyph (hb_codepoint_t glyph_index);
@@ -196,5 +198,4 @@ struct hb_buffer_t {
 	HB_BUFFER_XALLOCATE_VAR (b, deallocate_var, var (), #var)
 
 
-
 #endif /* HB_BUFFER_PRIVATE_HH */
diff --git a/src/hb-buffer.cc b/src/hb-buffer.cc
index f8c62ac..b9623e8 100644
--- a/src/hb-buffer.cc
+++ b/src/hb-buffer.cc
@@ -134,6 +134,7 @@ hb_buffer_t::get_scratch_buffer (unsigned int *size)
 }
 
 
+
 /* HarfBuzz-Internal API */
 
 void
@@ -234,12 +235,13 @@ hb_buffer_t::swap_buffers (void)
   idx = 0;
 }
 
+
 void
 hb_buffer_t::replace_glyphs (unsigned int num_in,
 			     unsigned int num_out,
 			     const uint32_t *glyph_data)
 {
-  if (!make_room_for (num_in, num_out)) return;
+  if (unlikely (!make_room_for (num_in, num_out))) return;
 
   merge_clusters (idx, idx + num_in);
 
@@ -259,7 +261,7 @@ hb_buffer_t::replace_glyphs (unsigned int num_in,
 void
 hb_buffer_t::output_glyph (hb_codepoint_t glyph_index)
 {
-  if (!make_room_for (0, 1)) return;
+  if (unlikely (!make_room_for (0, 1))) return;
 
   out_info[out_len] = info[idx];
   out_info[out_len].codepoint = glyph_index;
@@ -270,7 +272,7 @@ hb_buffer_t::output_glyph (hb_codepoint_t glyph_index)
 void
 hb_buffer_t::copy_glyph (void)
 {
-  if (!make_room_for (0, 1)) return;
+  if (unlikely (!make_room_for (0, 1))) return;
 
   out_info[out_len] = info[idx];
 
@@ -280,9 +282,10 @@ hb_buffer_t::copy_glyph (void)
 void
 hb_buffer_t::replace_glyph (hb_codepoint_t glyph_index)
 {
-  if (!make_room_for (1, 1)) return;
-
-  out_info[out_len] = info[idx];
+  if (unlikely (out_info != info || out_len != idx)) {
+    if (unlikely (!make_room_for (1, 1))) return;
+    out_info[out_len] = info[idx];
+  }
   out_info[out_len].codepoint = glyph_index;
 
   idx++;
@@ -294,20 +297,17 @@ hb_buffer_t::next_glyph (void)
 {
   if (have_output)
   {
-    if (out_info != info)
-    {
-      if (unlikely (!ensure (out_len + 1))) return;
+    if (unlikely (out_info != info || out_len != idx)) {
+      if (unlikely (!make_room_for (1, 1))) return;
       out_info[out_len] = info[idx];
     }
-    else if (out_len != idx)
-      out_info[out_len] = info[idx];
-
     out_len++;
   }
 
   idx++;
 }
 
+
 void
 hb_buffer_t::set_masks (hb_mask_t    value,
 			hb_mask_t    mask,
commit 693918ef8541014a5ef7dfb91c6ea0ae36d9c368
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Mon Jul 30 21:08:51 2012 -0400

    [OT] Streamline complex shaper enumeration
    
    Add a shaper class struct.

diff --git a/src/hb-ot-shape-complex-arabic.cc b/src/hb-ot-shape-complex-arabic.cc
index 1f63c12..0f73d6d 100644
--- a/src/hb-ot-shape-complex-arabic.cc
+++ b/src/hb-ot-shape-complex-arabic.cc
@@ -164,9 +164,10 @@ static const struct arabic_state_table_entry {
 
 
 
-void
-_hb_ot_shape_complex_collect_features_arabic (hb_ot_map_builder_t *map,
-					      const hb_segment_properties_t *props)
+static void
+collect_features_arabic (const hb_ot_complex_shaper_t  *shaper,
+			 hb_ot_map_builder_t           *map,
+			 const hb_segment_properties_t *props)
 {
   /* For Language forms (in ArabicOT speak), we do the iso/fina/medi/init together,
    * then rlig and calt each in their own stage.  This makes IranNastaliq's ALLAH
@@ -199,18 +200,6 @@ _hb_ot_shape_complex_collect_features_arabic (hb_ot_map_builder_t *map,
   map->add_bool_feature (HB_TAG('c','s','w','h'));
 }
 
-void
-_hb_ot_shape_complex_override_features_arabic (hb_ot_map_builder_t *map,
-					       const hb_segment_properties_t *props)
-{
-}
-
-hb_ot_shape_normalization_mode_t
-_hb_ot_shape_complex_normalization_preference_arabic (const hb_segment_properties_t *props)
-{
-  return HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS;
-}
-
 
 static void
 arabic_fallback_shape (hb_font_t *font, hb_buffer_t *buffer)
@@ -246,10 +235,11 @@ arabic_fallback_shape (hb_font_t *font, hb_buffer_t *buffer)
   buffer->swap_buffers ();
 }
 
-void
-_hb_ot_shape_complex_setup_masks_arabic (hb_ot_map_t *map,
-					 hb_buffer_t *buffer,
-					 hb_font_t *font)
+static void
+setup_masks_arabic (const hb_ot_complex_shaper_t *shaper,
+		    const hb_ot_map_t            *map,
+		    hb_buffer_t                  *buffer,
+		    hb_font_t                    *font)
 {
   unsigned int count = buffer->len;
   unsigned int prev = 0, state = 0;
@@ -302,4 +292,11 @@ _hb_ot_shape_complex_setup_masks_arabic (hb_ot_map_t *map,
   HB_BUFFER_DEALLOCATE_VAR (buffer, arabic_shaping_action);
 }
 
-
+const hb_ot_complex_shaper_t _hb_ot_complex_shaper_arabic =
+{
+  "arabic",
+  collect_features_arabic,
+  NULL, /* override_features */
+  NULL, /* normalization_preference */
+  setup_masks_arabic,
+};
diff --git a/src/hb-ot-shape-complex-indic.cc b/src/hb-ot-shape-complex-indic.cc
index c7025ff..b384e0e 100644
--- a/src/hb-ot-shape-complex-indic.cc
+++ b/src/hb-ot-shape-complex-indic.cc
@@ -89,8 +89,11 @@ compare_codepoint (const void *pa, const void *pb)
 }
 
 static bool
-would_substitute (hb_codepoint_t *glyphs, unsigned int glyphs_count,
-		  hb_tag_t feature_tag, hb_ot_map_t *map, hb_face_t *face)
+would_substitute (hb_codepoint_t    *glyphs,
+		  unsigned int       glyphs_count,
+		  hb_tag_t           feature_tag,
+		  const hb_ot_map_t *map,
+		  hb_face_t         *face)
 {
   unsigned int lookup_indices[32];
   unsigned int offset, len;
@@ -115,7 +118,9 @@ would_substitute (hb_codepoint_t *glyphs, unsigned int glyphs_count,
 }
 
 static indic_position_t
-consonant_position (hb_codepoint_t u, hb_ot_map_t *map, hb_font_t *font)
+consonant_position (hb_codepoint_t     u,
+		    const hb_ot_map_t *map,
+		    hb_font_t         *font)
 {
   if ((u & ~0x007F) == 0x1780)
     return POS_BELOW_C; /* In Khmer coeng model, all are subjoining. */
@@ -232,7 +237,9 @@ is_halant_or_coeng (const hb_glyph_info_t &info)
 }
 
 static inline void
-set_indic_properties (hb_glyph_info_t &info, hb_ot_map_t *map, hb_font_t *font)
+set_indic_properties (hb_glyph_info_t   &info,
+		      const hb_ot_map_t *map,
+		      hb_font_t         *font)
 {
   hb_codepoint_t u = info.codepoint;
   unsigned int type = get_indic_categories (u);
@@ -387,9 +394,10 @@ final_reordering (const hb_ot_map_t *map,
 		  hb_buffer_t *buffer,
 		  void *user_data HB_UNUSED);
 
-void
-_hb_ot_shape_complex_collect_features_indic (hb_ot_map_builder_t *map,
-					     const hb_segment_properties_t *props HB_UNUSED)
+static void
+collect_features_indic (const hb_ot_complex_shaper_t  *shaper,
+			hb_ot_map_builder_t           *map,
+			const hb_segment_properties_t *props)
 {
   map->add_bool_feature (HB_TAG('l','o','c','l'));
   /* The Indic specs do not require ccmp, but we apply it here since if
@@ -409,9 +417,10 @@ _hb_ot_shape_complex_collect_features_indic (hb_ot_map_builder_t *map,
     map->add_bool_feature (indic_other_features[i].tag, indic_other_features[i].is_global);
 }
 
-void
-_hb_ot_shape_complex_override_features_indic (hb_ot_map_builder_t *map,
-					      const hb_segment_properties_t *props HB_UNUSED)
+static void
+override_features_indic (const hb_ot_complex_shaper_t  *shaper,
+			 hb_ot_map_builder_t           *map,
+			 const hb_segment_properties_t *props)
 {
   /* Uniscribe does not apply 'kern'. */
   if (indic_options ().uniscribe_bug_compatible)
@@ -426,10 +435,11 @@ _hb_ot_shape_complex_normalization_preference_indic (const hb_segment_properties
 }
 
 
-void
-_hb_ot_shape_complex_setup_masks_indic (hb_ot_map_t *map,
-					hb_buffer_t *buffer,
-					hb_font_t *font)
+static void
+setup_masks_indic (const hb_ot_complex_shaper_t *shaper,
+		   const hb_ot_map_t            *map,
+		   hb_buffer_t                  *buffer,
+		   hb_font_t                    *font)
 {
   HB_BUFFER_ALLOCATE_VAR (buffer, indic_category);
   HB_BUFFER_ALLOCATE_VAR (buffer, indic_position);
@@ -1235,4 +1245,11 @@ final_reordering (const hb_ot_map_t *map,
 }
 
 
-
+const hb_ot_complex_shaper_t _hb_ot_complex_shaper_indic =
+{
+  "indic",
+  collect_features_indic,
+  override_features_indic,
+  NULL, /* normalization_preference */
+  setup_masks_indic,
+};
diff --git a/src/hb-ot-shape-complex-misc.cc b/src/hb-ot-shape-complex-misc.cc
index 4b9e6a6..1815366 100644
--- a/src/hb-ot-shape-complex-misc.cc
+++ b/src/hb-ot-shape-complex-misc.cc
@@ -49,9 +49,10 @@ static const hb_tag_t tibetan_features[] =
   HB_TAG_NONE
 };
 
-void
-_hb_ot_shape_complex_collect_features_default (hb_ot_map_builder_t *map HB_UNUSED,
-					       const hb_segment_properties_t *props)
+static void
+collect_features_default (const hb_ot_complex_shaper_t  *shaper,
+			  hb_ot_map_builder_t           *map,
+			  const hb_segment_properties_t *props)
 {
   const hb_tag_t *script_features = NULL;
 
@@ -72,14 +73,9 @@ _hb_ot_shape_complex_collect_features_default (hb_ot_map_builder_t *map HB_UNUSE
     map->add_bool_feature (*script_features);
 }
 
-void
-_hb_ot_shape_complex_override_features_default (hb_ot_map_builder_t *map HB_UNUSED,
-					        const hb_segment_properties_t *props HB_UNUSED)
-{
-}
-
-hb_ot_shape_normalization_mode_t
-_hb_ot_shape_complex_normalization_preference_default (const hb_segment_properties_t *props)
+static hb_ot_shape_normalization_mode_t
+normalization_preference_default (const hb_ot_complex_shaper_t  *shaper,
+				  const hb_segment_properties_t *props)
 {
   switch ((hb_tag_t) props->script)
   {
@@ -90,39 +86,23 @@ _hb_ot_shape_complex_normalization_preference_default (const hb_segment_properti
   return HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS;
 }
 
-void
-_hb_ot_shape_complex_setup_masks_default (hb_ot_map_t *map HB_UNUSED,
-					  hb_buffer_t *buffer HB_UNUSED,
-					  hb_font_t *font HB_UNUSED)
+const hb_ot_complex_shaper_t _hb_ot_complex_shaper_default =
 {
-}
-
+  "default",
+  collect_features_default,
+  NULL, /* override_features */
+  normalization_preference_default,
+  NULL, /* setup_masks */
+};
 
 
 /* Thai / Lao shaper */
 
-void
-_hb_ot_shape_complex_collect_features_thai (hb_ot_map_builder_t *map HB_UNUSED,
-					    const hb_segment_properties_t *props HB_UNUSED)
-{
-}
-
-void
-_hb_ot_shape_complex_override_features_thai (hb_ot_map_builder_t *map HB_UNUSED,
-					     const hb_segment_properties_t *props HB_UNUSED)
-{
-}
-
-hb_ot_shape_normalization_mode_t
-_hb_ot_shape_complex_normalization_preference_thai (const hb_segment_properties_t *props HB_UNUSED)
-{
-  return HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS;
-}
-
-void
-_hb_ot_shape_complex_setup_masks_thai (hb_ot_map_t *map HB_UNUSED,
-				       hb_buffer_t *buffer,
-				       hb_font_t *font HB_UNUSED)
+static void
+setup_masks_thai (const hb_ot_complex_shaper_t *shaper,
+		  const hb_ot_map_t            *map,
+		  hb_buffer_t                  *buffer,
+		  hb_font_t                    *font)
 {
   /* The following is NOT specified in the MS OT Thai spec, however, it seems
    * to be what Uniscribe and other engines implement.  According to Eric Muller:
@@ -213,3 +193,12 @@ _hb_ot_shape_complex_setup_masks_thai (hb_ot_map_t *map HB_UNUSED,
   }
   buffer->swap_buffers ();
 }
+
+const hb_ot_complex_shaper_t _hb_ot_complex_shaper_thai =
+{
+  "thai",
+  NULL, /* collect_features */
+  NULL, /* override_features */
+  NULL, /* normalization_preference */
+  setup_masks_thai,
+};
diff --git a/src/hb-ot-shape-complex-private.hh b/src/hb-ot-shape-complex-private.hh
index 689ca61..c0864fe 100644
--- a/src/hb-ot-shape-complex-private.hh
+++ b/src/hb-ot-shape-complex-private.hh
@@ -44,6 +44,8 @@
 #define complex_var_temporary_u8()	var2.u8[0]
 
 
+
+/* Master OT shaper list */
 #define HB_COMPLEX_SHAPERS_IMPLEMENT_SHAPERS \
   HB_COMPLEX_SHAPER_IMPLEMENT (default) /* should be first */ \
   HB_COMPLEX_SHAPER_IMPLEMENT (arabic) \
@@ -51,21 +53,60 @@
   HB_COMPLEX_SHAPER_IMPLEMENT (thai) \
   /* ^--- Add new shapers here */
 
-enum hb_ot_complex_shaper_t {
-#define HB_COMPLEX_SHAPER_IMPLEMENT(name) hb_ot_complex_shaper_##name,
-  HB_COMPLEX_SHAPERS_IMPLEMENT_SHAPERS
-  /* Just here to avoid enum trailing comma: */
-  hb_ot_complex_shaper_generic = hb_ot_complex_shaper_default
-#undef HB_COMPLEX_SHAPER_IMPLEMENT
+
+struct hb_ot_complex_shaper_t
+{
+  char name[8];
+
+  /* collect_features()
+   * Called during shape_plan().
+   * Shapers should use map to add their features and callbacks.
+   * May be NULL.
+   */
+  void (*collect_features) (const hb_ot_complex_shaper_t  *shaper,
+			    hb_ot_map_builder_t           *map,
+			    const hb_segment_properties_t *props);
+
+  /* override_features()
+   * Called during shape_plan().
+   * Shapers should use map to override features and add callbacks after
+   * common features are added.
+   * May be NULL.
+   */
+  void (*override_features) (const hb_ot_complex_shaper_t  *shaper,
+			     hb_ot_map_builder_t           *map,
+			     const hb_segment_properties_t *props);
+
+  /* normalization_preference()
+   * Called during shape_execute().
+   */
+  hb_ot_shape_normalization_mode_t
+  (*normalization_preference) (const hb_ot_complex_shaper_t  *shaper,
+			       const hb_segment_properties_t *props);
+
+
+  /* setup_masks()
+   * Called during shape_execute().
+   * Shapers should use map to get feature masks and set on buffer.
+   */
+  void (*setup_masks) (const hb_ot_complex_shaper_t *shaper,
+		       const hb_ot_map_t            *map,
+		       hb_buffer_t                  *buffer,
+		       hb_font_t                    *font);
 };
 
-static inline hb_ot_complex_shaper_t
+#define HB_COMPLEX_SHAPER_IMPLEMENT(name) extern HB_INTERNAL const hb_ot_complex_shaper_t _hb_ot_complex_shaper_##name;
+HB_COMPLEX_SHAPERS_IMPLEMENT_SHAPERS
+#undef HB_COMPLEX_SHAPER_IMPLEMENT
+
+
+static inline const hb_ot_complex_shaper_t *
 hb_ot_shape_complex_categorize (const hb_segment_properties_t *props)
 {
   switch ((hb_tag_t) props->script)
   {
     default:
-      return hb_ot_complex_shaper_default;
+      return &_hb_ot_complex_shaper_default;
 
 
     /* Unicode-1.1 additions */
@@ -79,14 +120,14 @@ hb_ot_shape_complex_categorize (const hb_segment_properties_t *props)
     /* Unicode-6.0 additions */
     case HB_SCRIPT_MANDAIC:
 
-      return hb_ot_complex_shaper_arabic;
+      return &_hb_ot_complex_shaper_arabic;
 
 
     /* Unicode-1.1 additions */
     case HB_SCRIPT_THAI:
     case HB_SCRIPT_LAO:
 
-      return hb_ot_complex_shaper_thai;
+      return &_hb_ot_complex_shaper_thai;
 
 
 
@@ -201,125 +242,9 @@ hb_ot_shape_complex_categorize (const hb_segment_properties_t *props)
     case HB_SCRIPT_SHARADA:
     case HB_SCRIPT_TAKRI:
 
-      return hb_ot_complex_shaper_indic;
-  }
-}
-
-
-
-/*
- * collect_features()
- *
- * Called during shape_plan().
- *
- * Shapers should use map to add their features and callbacks.
- */
-
-typedef void hb_ot_shape_complex_collect_features_func_t (hb_ot_map_builder_t *map, const hb_segment_properties_t  *props);
-#define HB_COMPLEX_SHAPER_IMPLEMENT(name) \
-  HB_INTERNAL hb_ot_shape_complex_collect_features_func_t _hb_ot_shape_complex_collect_features_##name;
-  HB_COMPLEX_SHAPERS_IMPLEMENT_SHAPERS
-#undef HB_COMPLEX_SHAPER_IMPLEMENT
-
-static inline void
-hb_ot_shape_complex_collect_features (hb_ot_complex_shaper_t shaper,
-				      hb_ot_map_builder_t *map,
-				      const hb_segment_properties_t  *props)
-{
-  switch (shaper) {
-    default:
-#define HB_COMPLEX_SHAPER_IMPLEMENT(name) \
-    case hb_ot_complex_shaper_##name:	_hb_ot_shape_complex_collect_features_##name (map, props); return;
-    HB_COMPLEX_SHAPERS_IMPLEMENT_SHAPERS
-#undef HB_COMPLEX_SHAPER_IMPLEMENT
+      return &_hb_ot_complex_shaper_indic;
   }
 }
 
 
-/*
- * override_features()
- *
- * Called during shape_plan().
- *
- * Shapers should use map to override features and add callbacks after
- * common features are added.
- */
-
-typedef void hb_ot_shape_complex_override_features_func_t (hb_ot_map_builder_t *map, const hb_segment_properties_t  *props);
-#define HB_COMPLEX_SHAPER_IMPLEMENT(name) \
-  HB_INTERNAL hb_ot_shape_complex_override_features_func_t _hb_ot_shape_complex_override_features_##name;
-  HB_COMPLEX_SHAPERS_IMPLEMENT_SHAPERS
-#undef HB_COMPLEX_SHAPER_IMPLEMENT
-
-static inline void
-hb_ot_shape_complex_override_features (hb_ot_complex_shaper_t shaper,
-				       hb_ot_map_builder_t *map,
-				       const hb_segment_properties_t  *props)
-{
-  switch (shaper) {
-    default:
-#define HB_COMPLEX_SHAPER_IMPLEMENT(name) \
-    case hb_ot_complex_shaper_##name:	_hb_ot_shape_complex_override_features_##name (map, props); return;
-    HB_COMPLEX_SHAPERS_IMPLEMENT_SHAPERS
-#undef HB_COMPLEX_SHAPER_IMPLEMENT
-  }
-}
-
-
-/*
- * normalization_preference()
- *
- * Called during shape_execute().
- */
-
-typedef hb_ot_shape_normalization_mode_t hb_ot_shape_complex_normalization_preference_func_t (const hb_segment_properties_t *props HB_UNUSED);
-#define HB_COMPLEX_SHAPER_IMPLEMENT(name) \
-  HB_INTERNAL hb_ot_shape_complex_normalization_preference_func_t _hb_ot_shape_complex_normalization_preference_##name;
-  HB_COMPLEX_SHAPERS_IMPLEMENT_SHAPERS
-#undef HB_COMPLEX_SHAPER_IMPLEMENT
-
-static inline hb_ot_shape_normalization_mode_t
-hb_ot_shape_complex_normalization_preference (hb_ot_complex_shaper_t shaper,
-					      const hb_segment_properties_t *props)
-{
-  switch (shaper) {
-    default:
-#define HB_COMPLEX_SHAPER_IMPLEMENT(name) \
-    case hb_ot_complex_shaper_##name:	return _hb_ot_shape_complex_normalization_preference_##name (props);
-    HB_COMPLEX_SHAPERS_IMPLEMENT_SHAPERS
-#undef HB_COMPLEX_SHAPER_IMPLEMENT
-  }
-}
-
-
-/* setup_masks()
- *
- * Called during shape_execute().
- *
- * Shapers should use map to get feature masks and set on buffer.
- */
-
-typedef void hb_ot_shape_complex_setup_masks_func_t (hb_ot_map_t *map, hb_buffer_t *buffer, hb_font_t *font);
-#define HB_COMPLEX_SHAPER_IMPLEMENT(name) \
-  HB_INTERNAL hb_ot_shape_complex_setup_masks_func_t _hb_ot_shape_complex_setup_masks_##name;
-  HB_COMPLEX_SHAPERS_IMPLEMENT_SHAPERS
-#undef HB_COMPLEX_SHAPER_IMPLEMENT
-
-static inline void
-hb_ot_shape_complex_setup_masks (hb_ot_complex_shaper_t shaper,
-				 hb_ot_map_t *map,
-				 hb_buffer_t *buffer,
-				 hb_font_t *font)
-{
-  switch (shaper) {
-    default:
-#define HB_COMPLEX_SHAPER_IMPLEMENT(name) \
-    case hb_ot_complex_shaper_##name:	_hb_ot_shape_complex_setup_masks_##name (map, buffer, font); return;
-    HB_COMPLEX_SHAPERS_IMPLEMENT_SHAPERS
-#undef HB_COMPLEX_SHAPER_IMPLEMENT
-  }
-}
-
-
-
 #endif /* HB_OT_SHAPE_COMPLEX_PRIVATE_HH */
diff --git a/src/hb-ot-shape-normalize-private.hh b/src/hb-ot-shape-normalize-private.hh
index bb81f00..4c89a8f 100644
--- a/src/hb-ot-shape-normalize-private.hh
+++ b/src/hb-ot-shape-normalize-private.hh
@@ -36,7 +36,9 @@
 enum hb_ot_shape_normalization_mode_t {
   HB_OT_SHAPE_NORMALIZATION_MODE_DECOMPOSED,
   HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS, /* never composes base-to-base */
-  HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_FULL /* including base-to-base composition */
+  HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_FULL, /* including base-to-base composition */
+
+  HB_OT_SHAPE_NORMALIZATION_MODE_DEFAULT = HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS,
 };
 
 HB_INTERNAL void _hb_ot_shape_normalize (hb_font_t *font,
diff --git a/src/hb-ot-shape-private.hh b/src/hb-ot-shape-private.hh
index e1f6a69..30c7808 100644
--- a/src/hb-ot-shape-private.hh
+++ b/src/hb-ot-shape-private.hh
@@ -36,7 +36,7 @@
 struct hb_ot_shape_plan_t
 {
   hb_ot_map_t map;
-  hb_ot_complex_shaper_t shaper;
+  const hb_ot_complex_shaper_t *shaper;
 };
 
 
diff --git a/src/hb-ot-shape.cc b/src/hb-ot-shape.cc
index 0cd548f..8df8251 100644
--- a/src/hb-ot-shape.cc
+++ b/src/hb-ot-shape.cc
@@ -73,7 +73,7 @@ hb_tag_t vertical_features[] = {
 struct hb_ot_shape_planner_t
 {
   hb_ot_map_builder_t map;
-  hb_ot_complex_shaper_t shaper;
+  const hb_ot_complex_shaper_t *shaper;
 
   hb_ot_shape_planner_t (void) : map () {}
   ~hb_ot_shape_planner_t (void) { map.finish (); }
@@ -118,7 +118,8 @@ hb_ot_shape_collect_features (hb_ot_shape_planner_t          *planner,
       planner->map.add_bool_feature (array[i]); \
   } HB_STMT_END
 
-  hb_ot_shape_complex_collect_features (planner->shaper, &planner->map, props);
+  if (planner->shaper->collect_features)
+    planner->shaper->collect_features (planner->shaper, &planner->map, props);
 
   ADD_FEATURES (common_features);
 
@@ -127,7 +128,8 @@ hb_ot_shape_collect_features (hb_ot_shape_planner_t          *planner,
   else
     ADD_FEATURES (vertical_features);
 
-  hb_ot_shape_complex_override_features (planner->shaper, &planner->map, props);
+  if (planner->shaper->override_features)
+    planner->shaper->override_features (planner->shaper, &planner->map, props);
 
 #undef ADD_FEATURES
 
@@ -233,7 +235,8 @@ hb_ot_shape_setup_masks (hb_ot_shape_context_t *c)
   hb_mask_t global_mask = c->plan->map.get_global_mask ();
   c->buffer->reset_masks (global_mask);
 
-  hb_ot_shape_complex_setup_masks (c->plan->shaper, &c->plan->map, c->buffer, c->font);
+  if (c->plan->shaper->setup_masks)
+    c->plan->shaper->setup_masks (c->plan->shaper, &c->plan->map, c->buffer, c->font);
 
   for (unsigned int i = 0; i < c->num_user_features; i++)
   {
@@ -493,8 +496,9 @@ hb_ot_shape_internal (hb_ot_shape_context_t *c)
   hb_ensure_native_direction (c->buffer);
 
   _hb_ot_shape_normalize (c->font, c->buffer,
-			  hb_ot_shape_complex_normalization_preference (c->plan->shaper,
-									&c->buffer->props));
+			  c->plan->shaper->normalization_preference ?
+			  c->plan->shaper->normalization_preference (c->plan->shaper, &c->buffer->props) :
+			  HB_OT_SHAPE_NORMALIZATION_MODE_DEFAULT);
 
   hb_ot_shape_setup_masks (c);
 
commit c2e42c3db691515f3a458eb4c71fe1e6439d5620
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Mon Jul 30 19:54:50 2012 -0400

    Minor

diff --git a/src/hb-ot-layout-gpos-table.hh b/src/hb-ot-layout-gpos-table.hh
index 53f2354..339749e 100644
--- a/src/hb-ot-layout-gpos-table.hh
+++ b/src/hb-ot-layout-gpos-table.hh
@@ -1671,7 +1671,7 @@ GPOS::position_finish (hb_font_t *font HB_UNUSED, hb_buffer_t *buffer)
 
   HB_BUFFER_DEALLOCATE_VAR (buffer, syllable);
   HB_BUFFER_DEALLOCATE_VAR (buffer, lig_props);
-  HB_BUFFER_DEALLOCATE_VAR (buffer, props_cache);
+  HB_BUFFER_DEALLOCATE_VAR (buffer, glyph_props);
 }
 
 
diff --git a/src/hb-ot-layout-gsub-table.hh b/src/hb-ot-layout-gsub-table.hh
index 328090a..05caee9 100644
--- a/src/hb-ot-layout-gsub-table.hh
+++ b/src/hb-ot-layout-gsub-table.hh
@@ -1311,7 +1311,7 @@ struct GSUB : GSUBGPOS
 void
 GSUB::substitute_start (hb_face_t *face, hb_buffer_t *buffer)
 {
-  HB_BUFFER_ALLOCATE_VAR (buffer, props_cache);
+  HB_BUFFER_ALLOCATE_VAR (buffer, glyph_props);
   HB_BUFFER_ALLOCATE_VAR (buffer, lig_props);
   HB_BUFFER_ALLOCATE_VAR (buffer, syllable);
 
@@ -1319,7 +1319,7 @@ GSUB::substitute_start (hb_face_t *face, hb_buffer_t *buffer)
   unsigned int count = buffer->len;
   for (unsigned int i = 0; i < count; i++) {
     buffer->info[i].lig_props() = buffer->info[i].syllable() = 0;
-    buffer->info[i].props_cache() = gdef.get_glyph_props (buffer->info[i].codepoint);
+    buffer->info[i].glyph_props() = gdef.get_glyph_props (buffer->info[i].codepoint);
   }
 }
 
diff --git a/src/hb-ot-layout-gsubgpos-private.hh b/src/hb-ot-layout-gsubgpos-private.hh
index b49cb6c..1f20514 100644
--- a/src/hb-ot-layout-gsubgpos-private.hh
+++ b/src/hb-ot-layout-gsubgpos-private.hh
@@ -271,7 +271,7 @@ struct hb_apply_context_t
   {
     unsigned int property;
 
-    property = info->props_cache();
+    property = info->glyph_props();
     *property_out = property;
 
     return match_properties (info->codepoint, property, lookup_props);
@@ -284,7 +284,7 @@ struct hb_apply_context_t
   {
     unsigned int property;
 
-    property = info->props_cache();
+    property = info->glyph_props();
     if (property_out)
       *property_out = property;
 
@@ -305,9 +305,9 @@ struct hb_apply_context_t
   inline void set_class (hb_codepoint_t glyph_index, unsigned int class_guess) const
   {
     if (likely (has_glyph_classes))
-      buffer->cur().props_cache() = gdef.get_glyph_props (glyph_index);
+      buffer->cur().glyph_props() = gdef.get_glyph_props (glyph_index);
     else if (class_guess)
-      buffer->cur().props_cache() = class_guess;
+      buffer->cur().glyph_props() = class_guess;
   }
 
   inline void output_glyph (hb_codepoint_t glyph_index,
diff --git a/src/hb-ot-layout-private.hh b/src/hb-ot-layout-private.hh
index 3da3dfe..fdbeb5b 100644
--- a/src/hb-ot-layout-private.hh
+++ b/src/hb-ot-layout-private.hh
@@ -38,7 +38,7 @@
 
 
 /* buffer var allocations, used during the GSUB/GPOS processing */
-#define props_cache()		var1.u16[1] /* GSUB/GPOS glyph_props cache */
+#define glyph_props()		var1.u16[1] /* GDEF glyph properties */
 #define syllable()		var2.u8[0] /* GSUB/GPOS shaping boundaries */
 #define lig_props()		var2.u8[1] /* GSUB/GPOS ligature tracking */
 
@@ -122,7 +122,7 @@ get_lig_comp (const hb_glyph_info_t &info)
 static inline unsigned int
 get_lig_num_comps (const hb_glyph_info_t &info)
 {
-  if ((info.props_cache() & HB_OT_LAYOUT_GLYPH_CLASS_LIGATURE) && is_a_ligature (info))
+  if ((info.glyph_props() & HB_OT_LAYOUT_GLYPH_CLASS_LIGATURE) && is_a_ligature (info))
     return info.lig_props() & 0x0F;
   else
     return 1;
commit 03f67bc012f42131b36083a23efc78e1b04b828c
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Mon Jul 30 19:47:53 2012 -0400

    More refactoring glyph class access

diff --git a/src/hb-ot-layout-gpos-table.hh b/src/hb-ot-layout-gpos-table.hh
index 61c5697..53f2354 100644
--- a/src/hb-ot-layout-gpos-table.hh
+++ b/src/hb-ot-layout-gpos-table.hh
@@ -1515,7 +1515,7 @@ struct PosLookup : Lookup
   {
     unsigned int lookup_type = get_type ();
 
-    if (!_hb_ot_layout_check_glyph_property (c->face, &c->buffer->cur(), c->lookup_props, &c->property))
+    if (!c->check_glyph_property (&c->buffer->cur(), c->lookup_props, &c->property))
       return false;
 
     unsigned int count = get_subtable_count ();
diff --git a/src/hb-ot-layout-gsub-table.hh b/src/hb-ot-layout-gsub-table.hh
index d62d97a..328090a 100644
--- a/src/hb-ot-layout-gsub-table.hh
+++ b/src/hb-ot-layout-gsub-table.hh
@@ -1179,7 +1179,7 @@ struct SubstLookup : Lookup
   {
     unsigned int lookup_type = get_type ();
 
-    if (!_hb_ot_layout_check_glyph_property (c->face, &c->buffer->cur(), c->lookup_props, &c->property))
+    if (!c->check_glyph_property (&c->buffer->cur(), c->lookup_props, &c->property))
       return false;
 
     unsigned int count = get_subtable_count ();
diff --git a/src/hb-ot-layout-gsubgpos-private.hh b/src/hb-ot-layout-gsubgpos-private.hh
index 4324d6d..b49cb6c 100644
--- a/src/hb-ot-layout-gsubgpos-private.hh
+++ b/src/hb-ot-layout-gsubgpos-private.hh
@@ -96,82 +96,6 @@ struct hb_would_apply_context_t
 	hb_auto_trace_t<HB_DEBUG_APPLY> trace (&c->debug_depth, "APPLY", this, HB_FUNC, "idx %d codepoint %u", c->buffer->idx, c->buffer->cur().codepoint);
 
 
-
-static inline hb_bool_t
-_hb_ot_layout_match_properties_mark (hb_face_t      *face,
-				     hb_codepoint_t  glyph,
-				     unsigned int    glyph_props,
-				     unsigned int    lookup_props)
-{
-  /* If using mark filtering sets, the high short of
-   * lookup_props has the set index.
-   */
-  if (lookup_props & LookupFlag::UseMarkFilteringSet)
-    return hb_ot_layout_from_face (face)->gdef->mark_set_covers (lookup_props >> 16, glyph);
-
-  /* The second byte of lookup_props has the meaning
-   * "ignore marks of attachment type different than
-   * the attachment type specified."
-   */
-  if (lookup_props & LookupFlag::MarkAttachmentType)
-    return (lookup_props & LookupFlag::MarkAttachmentType) == (glyph_props & LookupFlag::MarkAttachmentType);
-
-  return true;
-}
-
-static inline hb_bool_t
-_hb_ot_layout_match_properties (hb_face_t      *face,
-				hb_codepoint_t  glyph,
-				unsigned int    glyph_props,
-				unsigned int    lookup_props)
-{
-  /* Not covered, if, for example, glyph class is ligature and
-   * lookup_props includes LookupFlags::IgnoreLigatures
-   */
-  if (glyph_props & lookup_props & LookupFlag::IgnoreFlags)
-    return false;
-
-  if (unlikely (glyph_props & HB_OT_LAYOUT_GLYPH_CLASS_MARK))
-    return _hb_ot_layout_match_properties_mark (face, glyph, glyph_props, lookup_props);
-
-  return true;
-}
-
-static inline hb_bool_t
-_hb_ot_layout_check_glyph_property (hb_face_t    *face,
-				    hb_glyph_info_t *ginfo,
-				    unsigned int  lookup_props,
-				    unsigned int *property_out)
-{
-  unsigned int property;
-
-  property = ginfo->props_cache();
-  *property_out = property;
-
-  return _hb_ot_layout_match_properties (face, ginfo->codepoint, property, lookup_props);
-}
-
-static inline hb_bool_t
-_hb_ot_layout_skip_mark (hb_face_t    *face,
-			 hb_glyph_info_t *ginfo,
-			 unsigned int  lookup_props,
-			 unsigned int *property_out)
-{
-  unsigned int property;
-
-  property = ginfo->props_cache();
-  if (property_out)
-    *property_out = property;
-
-  /* If it's a mark, skip it if we don't accept it. */
-  if (unlikely (property & HB_OT_LAYOUT_GLYPH_CLASS_MARK))
-    return !_hb_ot_layout_match_properties (face, ginfo->codepoint, property, lookup_props);
-
-  /* If not a mark, don't skip. */
-  return false;
-}
-
-
 struct hb_apply_context_t
 {
   hb_font_t *font;
@@ -236,7 +160,7 @@ struct hb_apply_context_t
 	if (has_no_chance ())
 	  return false;
 	idx++;
-      } while (_hb_ot_layout_skip_mark (c->face, &c->buffer->info[idx], lookup_props, property_out));
+      } while (c->should_skip_mark (&c->buffer->info[idx], lookup_props, property_out));
       num_items--;
       return (c->buffer->info[idx].mask & mask) && (!syllable || syllable == c->buffer->info[idx].syllable ());
     }
@@ -285,7 +209,7 @@ struct hb_apply_context_t
 	if (has_no_chance ())
 	  return false;
 	idx--;
-      } while (_hb_ot_layout_skip_mark (c->face, &c->buffer->out_info[idx], lookup_props, property_out));
+      } while (c->should_skip_mark (&c->buffer->out_info[idx], lookup_props, property_out));
       num_items--;
       return (c->buffer->out_info[idx].mask & mask) && (!syllable || syllable == c->buffer->out_info[idx].syllable ());
     }
@@ -302,10 +226,80 @@ struct hb_apply_context_t
     uint8_t syllable;
   };
 
-  inline bool should_mark_skip_current_glyph (void) const
+  inline bool
+  match_properties_mark (hb_codepoint_t  glyph,
+			 unsigned int    glyph_props,
+			 unsigned int    lookup_props) const
+  {
+    /* If using mark filtering sets, the high short of
+     * lookup_props has the set index.
+     */
+    if (lookup_props & LookupFlag::UseMarkFilteringSet)
+      return gdef.mark_set_covers (lookup_props >> 16, glyph);
+
+    /* The second byte of lookup_props has the meaning
+     * "ignore marks of attachment type different than
+     * the attachment type specified."
+     */
+    if (lookup_props & LookupFlag::MarkAttachmentType)
+      return (lookup_props & LookupFlag::MarkAttachmentType) == (glyph_props & LookupFlag::MarkAttachmentType);
+
+    return true;
+  }
+
+  inline bool
+  match_properties (hb_codepoint_t  glyph,
+		    unsigned int    glyph_props,
+		    unsigned int    lookup_props) const
+  {
+    /* Not covered, if, for example, glyph class is ligature and
+     * lookup_props includes LookupFlags::IgnoreLigatures
+     */
+    if (glyph_props & lookup_props & LookupFlag::IgnoreFlags)
+      return false;
+
+    if (unlikely (glyph_props & HB_OT_LAYOUT_GLYPH_CLASS_MARK))
+      return match_properties_mark (glyph, glyph_props, lookup_props);
+
+    return true;
+  }
+
+  inline bool
+  check_glyph_property (hb_glyph_info_t *info,
+			unsigned int  lookup_props,
+			unsigned int *property_out) const
   {
     unsigned int property;
-    return _hb_ot_layout_skip_mark (face, &buffer->cur(), lookup_props, &property);
+
+    property = info->props_cache();
+    *property_out = property;
+
+    return match_properties (info->codepoint, property, lookup_props);
+  }
+
+  inline bool
+  should_skip_mark (hb_glyph_info_t *info,
+		   unsigned int  lookup_props,
+		   unsigned int *property_out) const
+  {
+    unsigned int property;
+
+    property = info->props_cache();
+    if (property_out)
+      *property_out = property;
+
+    /* If it's a mark, skip it if we don't accept it. */
+    if (unlikely (property & HB_OT_LAYOUT_GLYPH_CLASS_MARK))
+      return !match_properties (info->codepoint, property, lookup_props);
+
+    /* If not a mark, don't skip. */
+    return false;
+  }
+
+
+  inline bool should_mark_skip_current_glyph (void) const
+  {
+    return should_skip_mark (&buffer->cur(), lookup_props, NULL);
   }
 
   inline void set_class (hb_codepoint_t glyph_index, unsigned int class_guess) const
commit 300c7307eb7943ba7416b672345506be1e27c6ba
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Mon Jul 30 19:37:44 2012 -0400

    [OT] Don't crash if no GDEF available

diff --git a/src/hb-ot-layout-gsubgpos-private.hh b/src/hb-ot-layout-gsubgpos-private.hh
index 00c64d5..4324d6d 100644
--- a/src/hb-ot-layout-gsubgpos-private.hh
+++ b/src/hb-ot-layout-gsubgpos-private.hh
@@ -183,8 +183,8 @@ struct hb_apply_context_t
   unsigned int lookup_props;
   unsigned int property; /* propety of first glyph */
   unsigned int debug_depth;
-  bool has_glyph_classes;
   const GDEF &gdef;
+  bool has_glyph_classes;
 
 
   hb_apply_context_t (hb_font_t *font_,
@@ -196,8 +196,10 @@ struct hb_apply_context_t
 			lookup_mask (lookup_mask_),
 			nesting_level_left (MAX_NESTING_LEVEL),
 			lookup_props (0), property (0), debug_depth (0),
-			has_glyph_classes (hb_ot_layout_has_glyph_classes (face_)),
-			gdef (*hb_ot_layout_from_face (face_)->gdef /* XXX Unsafe dereference */) {}
+			gdef (hb_ot_layout_from_face (face_) &&
+			      !HB_SHAPER_DATA_IS_INVALID (hb_ot_layout_from_face (face_)) ?
+			      *hb_ot_layout_from_face (face_)->gdef : Null(GDEF)),
+			has_glyph_classes (gdef.has_glyph_classes ()) {}
 
   void set_lookup (const Lookup &l) {
     lookup_props = l.get_props ();
commit 3dcbdc2125c04c173f29f04922fc031929893f4e
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Mon Jul 30 19:31:17 2012 -0400

    Minor

diff --git a/src/hb-ot-layout.cc b/src/hb-ot-layout.cc
index 1e57874..28c2f83 100644
--- a/src/hb-ot-layout.cc
+++ b/src/hb-ot-layout.cc
@@ -42,17 +42,12 @@
 
 HB_SHAPER_DATA_ENSURE_DECLARE(ot, face)
 
-static hb_bool_t
-hb_ot_layout_ensure (hb_face_t *face)
-{
-  return hb_ot_shaper_face_data_ensure (face);
-}
-
-
 hb_ot_layout_t *
 _hb_ot_layout_create (hb_face_t *face)
 {
   hb_ot_layout_t *layout = (hb_ot_layout_t *) calloc (1, sizeof (hb_ot_layout_t));
+  if (unlikely (!layout))
+    return NULL;
 
   layout->gdef_blob = Sanitizer<GDEF>::sanitize (hb_face_reference_table (face, HB_OT_TAG_GDEF));
   layout->gdef = Sanitizer<GDEF>::lock_instance (layout->gdef_blob);
@@ -79,19 +74,19 @@ _hb_ot_layout_destroy (hb_ot_layout_t *layout)
 static inline const GDEF&
 _get_gdef (hb_face_t *face)
 {
-  if (unlikely (!hb_ot_layout_ensure (face))) return Null(GDEF);
+  if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return Null(GDEF);
   return *hb_ot_layout_from_face (face)->gdef;
 }
 static inline const GSUB&
 _get_gsub (hb_face_t *face)
 {
-  if (unlikely (!hb_ot_layout_ensure (face))) return Null(GSUB);
+  if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return Null(GSUB);
   return *hb_ot_layout_from_face (face)->gsub;
 }
 static inline const GPOS&
 _get_gpos (hb_face_t *face)
 {
-  if (unlikely (!hb_ot_layout_ensure (face))) return Null(GPOS);
+  if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return Null(GPOS);
   return *hb_ot_layout_from_face (face)->gpos;
 }
 
commit 05bd1b63426e07d1df7a1b40bf845dc94ab995a8
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Mon Jul 30 19:30:01 2012 -0400

    [GSUB/GPOS] Move glyph props matching around

diff --git a/src/hb-ot-layout-gpos-table.hh b/src/hb-ot-layout-gpos-table.hh
index 5945b0f..61c5697 100644
--- a/src/hb-ot-layout-gpos-table.hh
+++ b/src/hb-ot-layout-gpos-table.hh
@@ -1552,7 +1552,8 @@ struct PosLookup : Lookup
     else
       while (c->buffer->idx < c->buffer->len)
       {
-	if ((c->buffer->cur().mask & c->lookup_mask) && apply_once (c))
+	if ((c->buffer->cur().mask & c->lookup_mask) &&
+	    apply_once (c))
 	  ret = true;
 	else
 	  c->buffer->idx++;
@@ -1585,8 +1586,8 @@ struct GPOS : GSUBGPOS
   inline bool position_lookup (hb_apply_context_t *c, unsigned int lookup_index) const
   { return get_lookup (lookup_index).apply_string (c); }
 
-  static inline void position_start (hb_buffer_t *buffer);
-  static inline void position_finish (hb_buffer_t *buffer);
+  static inline void position_start (hb_font_t *font, hb_buffer_t *buffer);
+  static inline void position_finish (hb_font_t *font, hb_buffer_t *buffer);
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
@@ -1644,7 +1645,7 @@ fix_mark_attachment (hb_glyph_position_t *pos, unsigned int i, hb_direction_t di
 }
 
 void
-GPOS::position_start (hb_buffer_t *buffer)
+GPOS::position_start (hb_font_t *font HB_UNUSED, hb_buffer_t *buffer)
 {
   buffer->clear_positions ();
 
@@ -1654,7 +1655,7 @@ GPOS::position_start (hb_buffer_t *buffer)
 }
 
 void
-GPOS::position_finish (hb_buffer_t *buffer)
+GPOS::position_finish (hb_font_t *font HB_UNUSED, hb_buffer_t *buffer)
 {
   unsigned int len;
   hb_glyph_position_t *pos = hb_buffer_get_glyph_positions (buffer, &len);
diff --git a/src/hb-ot-layout-gsub-table.hh b/src/hb-ot-layout-gsub-table.hh
index 6a292a1..d62d97a 100644
--- a/src/hb-ot-layout-gsub-table.hh
+++ b/src/hb-ot-layout-gsub-table.hh
@@ -1220,7 +1220,8 @@ struct SubstLookup : Lookup
 	else
 	  while (c->buffer->idx < c->buffer->len)
 	  {
-	    if ((c->buffer->cur().mask & c->lookup_mask) && apply_once (c))
+	    if ((c->buffer->cur().mask & c->lookup_mask) &&
+		apply_once (c))
 	      ret = true;
 	    else
 	      c->buffer->next_glyph ();
@@ -1289,8 +1290,8 @@ struct GSUB : GSUBGPOS
   inline bool substitute_lookup (hb_apply_context_t *c, unsigned int lookup_index) const
   { return get_lookup (lookup_index).apply_string (c); }
 
-  static inline void substitute_start (hb_buffer_t *buffer);
-  static inline void substitute_finish (hb_buffer_t *buffer);
+  static inline void substitute_start (hb_face_t *face, hb_buffer_t *buffer);
+  static inline void substitute_finish (hb_face_t *face, hb_buffer_t *buffer);
 
   inline void closure_lookup (hb_closure_context_t *c,
 			      unsigned int          lookup_index) const
@@ -1308,19 +1309,22 @@ struct GSUB : GSUBGPOS
 
 
 void
-GSUB::substitute_start (hb_buffer_t *buffer)
+GSUB::substitute_start (hb_face_t *face, hb_buffer_t *buffer)
 {
   HB_BUFFER_ALLOCATE_VAR (buffer, props_cache);
   HB_BUFFER_ALLOCATE_VAR (buffer, lig_props);
   HB_BUFFER_ALLOCATE_VAR (buffer, syllable);
 
+  const GDEF &gdef = *hb_ot_layout_from_face (face)->gdef;
   unsigned int count = buffer->len;
-  for (unsigned int i = 0; i < count; i++)
-    buffer->info[i].props_cache() = buffer->info[i].lig_props() = buffer->info[i].syllable() = 0;
+  for (unsigned int i = 0; i < count; i++) {
+    buffer->info[i].lig_props() = buffer->info[i].syllable() = 0;
+    buffer->info[i].props_cache() = gdef.get_glyph_props (buffer->info[i].codepoint);
+  }
 }
 
 void
-GSUB::substitute_finish (hb_buffer_t *buffer HB_UNUSED)
+GSUB::substitute_finish (hb_face_t *face HB_UNUSED, hb_buffer_t *buffer HB_UNUSED)
 {
 }
 
diff --git a/src/hb-ot-layout-gsubgpos-private.hh b/src/hb-ot-layout-gsubgpos-private.hh
index 61dea3a..00c64d5 100644
--- a/src/hb-ot-layout-gsubgpos-private.hh
+++ b/src/hb-ot-layout-gsubgpos-private.hh
@@ -97,6 +97,81 @@ struct hb_would_apply_context_t
 
 
 
+static inline hb_bool_t
+_hb_ot_layout_match_properties_mark (hb_face_t      *face,
+				     hb_codepoint_t  glyph,
+				     unsigned int    glyph_props,
+				     unsigned int    lookup_props)
+{
+  /* If using mark filtering sets, the high short of
+   * lookup_props has the set index.
+   */
+  if (lookup_props & LookupFlag::UseMarkFilteringSet)
+    return hb_ot_layout_from_face (face)->gdef->mark_set_covers (lookup_props >> 16, glyph);
+
+  /* The second byte of lookup_props has the meaning
+   * "ignore marks of attachment type different than
+   * the attachment type specified."
+   */
+  if (lookup_props & LookupFlag::MarkAttachmentType)
+    return (lookup_props & LookupFlag::MarkAttachmentType) == (glyph_props & LookupFlag::MarkAttachmentType);
+
+  return true;
+}
+
+static inline hb_bool_t
+_hb_ot_layout_match_properties (hb_face_t      *face,
+				hb_codepoint_t  glyph,
+				unsigned int    glyph_props,
+				unsigned int    lookup_props)
+{
+  /* Not covered, if, for example, glyph class is ligature and
+   * lookup_props includes LookupFlags::IgnoreLigatures
+   */
+  if (glyph_props & lookup_props & LookupFlag::IgnoreFlags)
+    return false;
+
+  if (unlikely (glyph_props & HB_OT_LAYOUT_GLYPH_CLASS_MARK))
+    return _hb_ot_layout_match_properties_mark (face, glyph, glyph_props, lookup_props);
+
+  return true;
+}
+
+static inline hb_bool_t
+_hb_ot_layout_check_glyph_property (hb_face_t    *face,
+				    hb_glyph_info_t *ginfo,
+				    unsigned int  lookup_props,
+				    unsigned int *property_out)
+{
+  unsigned int property;
+
+  property = ginfo->props_cache();
+  *property_out = property;
+
+  return _hb_ot_layout_match_properties (face, ginfo->codepoint, property, lookup_props);
+}
+
+static inline hb_bool_t
+_hb_ot_layout_skip_mark (hb_face_t    *face,
+			 hb_glyph_info_t *ginfo,
+			 unsigned int  lookup_props,
+			 unsigned int *property_out)
+{
+  unsigned int property;
+
+  property = ginfo->props_cache();
+  if (property_out)
+    *property_out = property;
+
+  /* If it's a mark, skip it if we don't accept it. */
+  if (unlikely (property & HB_OT_LAYOUT_GLYPH_CLASS_MARK))
+    return !_hb_ot_layout_match_properties (face, ginfo->codepoint, property, lookup_props);
+
+  /* If not a mark, don't skip. */
+  return false;
+}
+
+
 struct hb_apply_context_t
 {
   hb_font_t *font;
@@ -109,6 +184,7 @@ struct hb_apply_context_t
   unsigned int property; /* propety of first glyph */
   unsigned int debug_depth;
   bool has_glyph_classes;
+  const GDEF &gdef;
 
 
   hb_apply_context_t (hb_font_t *font_,
@@ -120,7 +196,8 @@ struct hb_apply_context_t
 			lookup_mask (lookup_mask_),
 			nesting_level_left (MAX_NESTING_LEVEL),
 			lookup_props (0), property (0), debug_depth (0),
-			has_glyph_classes (hb_ot_layout_has_glyph_classes (face_)) {}
+			has_glyph_classes (hb_ot_layout_has_glyph_classes (face_)),
+			gdef (*hb_ot_layout_from_face (face_)->gdef /* XXX Unsafe dereference */) {}
 
   void set_lookup (const Lookup &l) {
     lookup_props = l.get_props ();
@@ -229,30 +306,30 @@ struct hb_apply_context_t
     return _hb_ot_layout_skip_mark (face, &buffer->cur(), lookup_props, &property);
   }
 
-  inline void set_klass_guess (unsigned int klass_guess) const
+  inline void set_class (hb_codepoint_t glyph_index, unsigned int class_guess) const
   {
     if (likely (has_glyph_classes))
-      buffer->cur().props_cache() = 0;
-    else if (klass_guess)
-      buffer->cur().props_cache() = klass_guess;
+      buffer->cur().props_cache() = gdef.get_glyph_props (glyph_index);
+    else if (class_guess)
+      buffer->cur().props_cache() = class_guess;
   }
 
   inline void output_glyph (hb_codepoint_t glyph_index,
-			    unsigned int klass_guess = 0) const
+			    unsigned int class_guess = 0) const
   {
-    set_klass_guess (klass_guess);
+    set_class (glyph_index, class_guess);
     buffer->output_glyph (glyph_index);
   }
   inline void replace_glyph (hb_codepoint_t glyph_index,
-			     unsigned int klass_guess = 0) const
+			     unsigned int class_guess = 0) const
   {
-    set_klass_guess (klass_guess);
+    set_class (glyph_index, class_guess);
     buffer->replace_glyph (glyph_index);
   }
   inline void replace_glyph_inplace (hb_codepoint_t glyph_index,
-				     unsigned int klass_guess = 0) const
+				     unsigned int class_guess = 0) const
   {
-    set_klass_guess (klass_guess);
+    set_class (glyph_index, class_guess);
     buffer->cur().codepoint = glyph_index;
   }
 };
diff --git a/src/hb-ot-layout-private.hh b/src/hb-ot-layout-private.hh
index 78c9d64..3da3dfe 100644
--- a/src/hb-ot-layout-private.hh
+++ b/src/hb-ot-layout-private.hh
@@ -57,18 +57,6 @@ typedef enum {
 } hb_ot_layout_glyph_class_t;
 
 
-HB_INTERNAL hb_bool_t
-_hb_ot_layout_check_glyph_property (hb_face_t    *face,
-				    hb_glyph_info_t *ginfo,
-				    unsigned int  lookup_props,
-				    unsigned int *property_out);
-
-HB_INTERNAL hb_bool_t
-_hb_ot_layout_skip_mark (hb_face_t    *face,
-			 hb_glyph_info_t *ginfo,
-			 unsigned int  lookup_props,
-			 unsigned int *property_out);
-
 
 /*
  * GSUB/GPOS
diff --git a/src/hb-ot-layout.cc b/src/hb-ot-layout.cc
index 2530340..1e57874 100644
--- a/src/hb-ot-layout.cc
+++ b/src/hb-ot-layout.cc
@@ -106,93 +106,6 @@ hb_ot_layout_has_glyph_classes (hb_face_t *face)
   return _get_gdef (face).has_glyph_classes ();
 }
 
-static inline unsigned int
-_hb_ot_layout_get_glyph_property (hb_face_t       *face,
-				  hb_glyph_info_t *info)
-{
-  if (!info->props_cache())
-  {
-    info->props_cache() = hb_ot_layout_from_face (face)->gdef->get_glyph_props (info->codepoint);
-  }
-
-  return info->props_cache();
-}
-
-static inline hb_bool_t
-_hb_ot_layout_match_properties_mark (hb_face_t      *face,
-				     hb_codepoint_t  glyph,
-				     unsigned int    glyph_props,
-				     unsigned int    lookup_props)
-{
-  /* If using mark filtering sets, the high short of
-   * lookup_props has the set index.
-   */
-  if (lookup_props & LookupFlag::UseMarkFilteringSet)
-    return hb_ot_layout_from_face (face)->gdef->mark_set_covers (lookup_props >> 16, glyph);
-
-  /* The second byte of lookup_props has the meaning
-   * "ignore marks of attachment type different than
-   * the attachment type specified."
-   */
-  if (lookup_props & LookupFlag::MarkAttachmentType)
-    return (lookup_props & LookupFlag::MarkAttachmentType) == (glyph_props & LookupFlag::MarkAttachmentType);
-
-  return true;
-}
-
-static inline hb_bool_t
-_hb_ot_layout_match_properties (hb_face_t      *face,
-				hb_codepoint_t  glyph,
-				unsigned int    glyph_props,
-				unsigned int    lookup_props)
-{
-  /* Not covered, if, for example, glyph class is ligature and
-   * lookup_props includes LookupFlags::IgnoreLigatures
-   */
-  if (glyph_props & lookup_props & LookupFlag::IgnoreFlags)
-    return false;
-
-  if (unlikely (glyph_props & HB_OT_LAYOUT_GLYPH_CLASS_MARK))
-    return _hb_ot_layout_match_properties_mark (face, glyph, glyph_props, lookup_props);
-
-  return true;
-}
-
-hb_bool_t
-_hb_ot_layout_check_glyph_property (hb_face_t    *face,
-				    hb_glyph_info_t *ginfo,
-				    unsigned int  lookup_props,
-				    unsigned int *property_out)
-{
-  unsigned int property;
-
-  property = _hb_ot_layout_get_glyph_property (face, ginfo);
-  *property_out = property;
-
-  return _hb_ot_layout_match_properties (face, ginfo->codepoint, property, lookup_props);
-}
-
-hb_bool_t
-_hb_ot_layout_skip_mark (hb_face_t    *face,
-			 hb_glyph_info_t *ginfo,
-			 unsigned int  lookup_props,
-			 unsigned int *property_out)
-{
-  unsigned int property;
-
-  property = _hb_ot_layout_get_glyph_property (face, ginfo);
-  if (property_out)
-    *property_out = property;
-
-  /* If it's a mark, skip it if we don't accept it. */
-  if (unlikely (property & HB_OT_LAYOUT_GLYPH_CLASS_MARK))
-    return !_hb_ot_layout_match_properties (face, ginfo->codepoint, property, lookup_props);
-
-  /* If not a mark, don't skip. */
-  return false;
-}
-
-
 
 unsigned int
 hb_ot_layout_get_attach_points (hb_face_t      *face,
@@ -215,6 +128,7 @@ hb_ot_layout_get_ligature_carets (hb_font_t      *font,
   return _get_gdef (font->face).get_lig_carets (font, direction, glyph, start_offset, caret_count, caret_array);
 }
 
+
 /*
  * GSUB/GPOS
  */
@@ -492,9 +406,9 @@ hb_ot_layout_would_substitute_lookup_fast (hb_face_t            *face,
 }
 
 void
-hb_ot_layout_substitute_start (hb_buffer_t  *buffer)
+hb_ot_layout_substitute_start (hb_face_t *face, hb_buffer_t *buffer)
 {
-  GSUB::substitute_start (buffer);
+  GSUB::substitute_start (face, buffer);
 }
 
 hb_bool_t
@@ -518,9 +432,9 @@ hb_ot_layout_substitute_lookup_fast (hb_face_t    *face,
 }
 
 void
-hb_ot_layout_substitute_finish (hb_buffer_t  *buffer HB_UNUSED)
+hb_ot_layout_substitute_finish (hb_face_t *face, hb_buffer_t *buffer)
 {
-  GSUB::substitute_finish (buffer);
+  GSUB::substitute_finish (face, buffer);
 }
 
 void
@@ -543,9 +457,9 @@ hb_ot_layout_has_positioning (hb_face_t *face)
 }
 
 void
-hb_ot_layout_position_start (hb_buffer_t  *buffer)
+hb_ot_layout_position_start (hb_font_t *font, hb_buffer_t *buffer)
 {
-  GPOS::position_start (buffer);
+  GPOS::position_start (font, buffer);
 }
 
 hb_bool_t
@@ -569,9 +483,9 @@ hb_ot_layout_position_lookup_fast (hb_font_t    *font,
 }
 
 void
-hb_ot_layout_position_finish (hb_buffer_t  *buffer)
+hb_ot_layout_position_finish (hb_font_t *font, hb_buffer_t *buffer)
 {
-  GPOS::position_finish (buffer);
+  GPOS::position_finish (font, buffer);
 }
 
 
diff --git a/src/hb-ot-layout.h b/src/hb-ot-layout.h
index 07e062f..4e205d7 100644
--- a/src/hb-ot-layout.h
+++ b/src/hb-ot-layout.h
@@ -178,7 +178,8 @@ hb_ot_layout_would_substitute_lookup (hb_face_t            *face,
 
 /* Should be called before all the substitute_lookup's are done. */
 void
-hb_ot_layout_substitute_start (hb_buffer_t  *buffer);
+hb_ot_layout_substitute_start (hb_face_t    *face,
+			       hb_buffer_t  *buffer);
 
 hb_bool_t
 hb_ot_layout_substitute_lookup (hb_face_t    *face,
@@ -188,7 +189,8 @@ hb_ot_layout_substitute_lookup (hb_face_t    *face,
 
 /* Should be called after all the substitute_lookup's are done */
 void
-hb_ot_layout_substitute_finish (hb_buffer_t  *buffer);
+hb_ot_layout_substitute_finish (hb_face_t    *face,
+				hb_buffer_t  *buffer);
 
 
 void
@@ -205,7 +207,8 @@ hb_ot_layout_has_positioning (hb_face_t *face);
 
 /* Should be called before all the position_lookup's are done.  Resets positions to zero. */
 void
-hb_ot_layout_position_start (hb_buffer_t  *buffer);
+hb_ot_layout_position_start (hb_font_t    *font,
+			     hb_buffer_t  *buffer);
 
 hb_bool_t
 hb_ot_layout_position_lookup (hb_font_t    *font,
@@ -215,7 +218,8 @@ hb_ot_layout_position_lookup (hb_font_t    *font,
 
 /* Should be called after all the position_lookup's are done */
 void
-hb_ot_layout_position_finish (hb_buffer_t  *buffer);
+hb_ot_layout_position_finish (hb_font_t    *font,
+			      hb_buffer_t  *buffer);
 
 
 HB_END_DECLS
diff --git a/src/hb-ot-shape.cc b/src/hb-ot-shape.cc
index c727fa6..0cd548f 100644
--- a/src/hb-ot-shape.cc
+++ b/src/hb-ot-shape.cc
@@ -342,8 +342,6 @@ hb_map_glyphs (hb_font_t    *font,
 static void
 hb_substitute_default (hb_ot_shape_context_t *c)
 {
-  hb_ot_layout_substitute_start (c->buffer);
-
   hb_mirror_chars (c);
 
   hb_map_glyphs (c->font, c->buffer);
@@ -352,11 +350,13 @@ hb_substitute_default (hb_ot_shape_context_t *c)
 static void
 hb_ot_substitute_complex (hb_ot_shape_context_t *c)
 {
+  hb_ot_layout_substitute_start (c->face, c->buffer);
+
   if (hb_ot_layout_has_substitution (c->face)) {
     c->plan->map.substitute (c->face, c->buffer);
   }
 
-  hb_ot_layout_substitute_finish (c->buffer);
+  hb_ot_layout_substitute_finish (c->face, c->buffer);
 
   return;
 }
@@ -367,7 +367,7 @@ hb_ot_substitute_complex (hb_ot_shape_context_t *c)
 static void
 hb_position_default (hb_ot_shape_context_t *c)
 {
-  hb_ot_layout_position_start (c->buffer);
+  hb_ot_layout_position_start (c->font, c->buffer);
 
   unsigned int count = c->buffer->len;
   for (unsigned int i = 0; i < count; i++) {
@@ -410,7 +410,7 @@ hb_ot_position_complex (hb_ot_shape_context_t *c)
     c->applied_position_complex = true;
   }
 
-  hb_ot_layout_position_finish (c->buffer);
+  hb_ot_layout_position_finish (c->font, c->buffer);
 
   return;
 }
commit 2fca1426ca06cabbe8f027f2dc9dee9c27560c76
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Mon Jul 30 18:46:41 2012 -0400

    [GSUB] Don't erase glyph classes if GDEF does not have glyph classes

diff --git a/src/hb-ot-layout-gsubgpos-private.hh b/src/hb-ot-layout-gsubgpos-private.hh
index e2423bc..61dea3a 100644
--- a/src/hb-ot-layout-gsubgpos-private.hh
+++ b/src/hb-ot-layout-gsubgpos-private.hh
@@ -231,7 +231,10 @@ struct hb_apply_context_t
 
   inline void set_klass_guess (unsigned int klass_guess) const
   {
-    buffer->cur().props_cache() = has_glyph_classes ? 0 : klass_guess;
+    if (likely (has_glyph_classes))
+      buffer->cur().props_cache() = 0;
+    else if (klass_guess)
+      buffer->cur().props_cache() = klass_guess;
   }
 
   inline void output_glyph (hb_codepoint_t glyph_index,
commit fd42257f8c45ff8e036e1c3eb1a788a101be7ead
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Mon Jul 30 18:40:27 2012 -0400

    Minor

diff --git a/src/hb-ot-layout.cc b/src/hb-ot-layout.cc
index 617034b..2530340 100644
--- a/src/hb-ot-layout.cc
+++ b/src/hb-ot-layout.cc
@@ -95,24 +95,6 @@ _get_gpos (hb_face_t *face)
   return *hb_ot_layout_from_face (face)->gpos;
 }
 
-static inline const GDEF&
-_get_gdef_fast (hb_face_t *face)
-{
-  return *hb_ot_layout_from_face (face)->gdef;
-}
-static inline const GSUB&
-_get_gsub_fast (hb_face_t *face)
-{
-  if (unlikely (!hb_ot_layout_ensure (face))) return Null(GSUB);
-  return *hb_ot_layout_from_face (face)->gsub;
-}
-static inline const GPOS&
-_get_gpos_fast (hb_face_t *face)
-{
-  if (unlikely (!hb_ot_layout_ensure (face))) return Null(GPOS);
-  return *hb_ot_layout_from_face (face)->gpos;
-}
-
 
 /*
  * GDEF
@@ -130,8 +112,7 @@ _hb_ot_layout_get_glyph_property (hb_face_t       *face,
 {
   if (!info->props_cache())
   {
-    const GDEF &gdef = _get_gdef_fast (face);
-    info->props_cache() = gdef.get_glyph_props (info->codepoint);
+    info->props_cache() = hb_ot_layout_from_face (face)->gdef->get_glyph_props (info->codepoint);
   }
 
   return info->props_cache();
@@ -147,7 +128,7 @@ _hb_ot_layout_match_properties_mark (hb_face_t      *face,
    * lookup_props has the set index.
    */
   if (lookup_props & LookupFlag::UseMarkFilteringSet)
-    return _get_gdef_fast (face).mark_set_covers (lookup_props >> 16, glyph);
+    return hb_ot_layout_from_face (face)->gdef->mark_set_covers (lookup_props >> 16, glyph);
 
   /* The second byte of lookup_props has the meaning
    * "ignore marks of attachment type different than
@@ -507,7 +488,7 @@ hb_ot_layout_would_substitute_lookup_fast (hb_face_t            *face,
 {
   if (unlikely (glyphs_length < 1 || glyphs_length > 2)) return false;
   hb_would_apply_context_t c (face, glyphs[0], glyphs_length == 2 ? glyphs[1] : -1);
-  return _get_gsub_fast (face).would_substitute_lookup (&c, lookup_index);
+  return hb_ot_layout_from_face (face)->gsub->would_substitute_lookup (&c, lookup_index);
 }
 
 void
@@ -533,7 +514,7 @@ hb_ot_layout_substitute_lookup_fast (hb_face_t    *face,
 				     hb_mask_t     mask)
 {
   hb_apply_context_t c (NULL, face, buffer, mask);
-  return _get_gsub_fast (face).substitute_lookup (&c, lookup_index);
+  return hb_ot_layout_from_face (face)->gsub->substitute_lookup (&c, lookup_index);
 }
 
 void
@@ -584,7 +565,7 @@ hb_ot_layout_position_lookup_fast (hb_font_t    *font,
 				   hb_mask_t     mask)
 {
   hb_apply_context_t c (font, font->face, buffer, mask);
-  return _get_gpos_fast (font->face).position_lookup (&c, lookup_index);
+  return hb_ot_layout_from_face (font->face)->gpos->position_lookup (&c, lookup_index);
 }
 
 void
commit 7fbbf86efe675e4c038dfc5985c24bbc544620cd
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Mon Jul 30 18:36:42 2012 -0400

    [GSUB] Minor

diff --git a/src/hb-ot-layout-gsub-table.hh b/src/hb-ot-layout-gsub-table.hh
index bdc7b38..6a292a1 100644
--- a/src/hb-ot-layout-gsub-table.hh
+++ b/src/hb-ot-layout-gsub-table.hh
@@ -920,7 +920,7 @@ struct ReverseChainSingleSubstFormat1
 			 match_coverage, this,
 			 1))
     {
-      c->buffer->cur().codepoint = substitute[index];
+      c->replace_glyph_inplace (substitute[index]);
       c->buffer->idx--; /* Reverse! */
       return TRACE_RETURN (true);
     }
diff --git a/src/hb-ot-layout-gsubgpos-private.hh b/src/hb-ot-layout-gsubgpos-private.hh
index 6dc2f16..e2423bc 100644
--- a/src/hb-ot-layout-gsubgpos-private.hh
+++ b/src/hb-ot-layout-gsubgpos-private.hh
@@ -246,6 +246,12 @@ struct hb_apply_context_t
     set_klass_guess (klass_guess);
     buffer->replace_glyph (glyph_index);
   }
+  inline void replace_glyph_inplace (hb_codepoint_t glyph_index,
+				     unsigned int klass_guess = 0) const
+  {
+    set_klass_guess (klass_guess);
+    buffer->cur().codepoint = glyph_index;
+  }
 };
 
 



More information about the HarfBuzz mailing list