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

Behdad Esfahbod behdad at kemper.freedesktop.org
Fri May 11 13:02:38 PDT 2012


 configure.ac                                                                               |    2 
 src/hb-object-private.hh                                                                   |    9 
 src/hb-open-file-private.hh                                                                |   23 
 src/hb-open-type-private.hh                                                                |  122 -
 src/hb-ot-head-table.hh                                                                    |    4 
 src/hb-ot-hhea-table.hh                                                                    |    4 
 src/hb-ot-hmtx-table.hh                                                                    |    4 
 src/hb-ot-layout-common-private.hh                                                         |   51 
 src/hb-ot-layout-gdef-table.hh                                                             |   48 
 src/hb-ot-layout-gpos-table.hh                                                             |  356 ++---
 src/hb-ot-layout-gsub-table.hh                                                             |  225 +--
 src/hb-ot-layout-gsubgpos-private.hh                                                       |  222 +--
 src/hb-ot-layout-private.hh                                                                |    4 
 src/hb-ot-maxp-table.hh                                                                    |    7 
 src/hb-ot-name-table.hh                                                                    |   17 
 src/hb-ot-shape-complex-arabic.cc                                                          |    5 
 src/hb-ot-shape-complex-indic-machine.rl                                                   |   46 
 src/hb-ot-shape-complex-indic-private.hh                                                   |  244 +--
 src/hb-ot-shape-complex-indic.cc                                                           |  671 +++++++---
 src/hb-ot-shape-complex-private.hh                                                         |   20 
 src/hb-ot-shape-normalize.cc                                                               |   25 
 src/hb-ot-shape-private.hh                                                                 |   27 
 src/hb-ot-shape.cc                                                                         |   41 
 src/hb-private.hh                                                                          |  157 +-
 src/hb-set-private.hh                                                                      |    2 
 src/hb-unicode-private.hh                                                                  |   38 
 src/hb-uniscribe.cc                                                                        |    3 
 src/indic.cc                                                                               |    2 
 test/shaping/texts/in-tree/shaper-indic/indic/script-devanagari/misc/MANIFEST              |    4 
 test/shaping/texts/in-tree/shaper-indic/indic/script-devanagari/misc/dottedcircle.txt      |    7 
 test/shaping/texts/in-tree/shaper-indic/indic/script-devanagari/misc/joiners.txt           |   19 
 test/shaping/texts/in-tree/shaper-indic/indic/script-devanagari/misc/spec-deviations.txt   |    1 
 test/shaping/texts/in-tree/shaper-indic/indic/script-devanagari/misc/tricky-reordering.txt |    5 
 33 files changed, 1427 insertions(+), 988 deletions(-)

New commits:
commit f7e8dcfd4fc377e3d786b097beb656284240456d
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Fri May 11 22:00:06 2012 +0200

    [Indic] Unbreak Devanagari
    
    And this, concludes the HarfBuzz Massala Hackfest.
    
    I like to specially thank Jonathan Kew for doing all the decription and
    letting me get commit points.

diff --git a/configure.ac b/configure.ac
index 319d480..7617af2 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,6 +1,6 @@
 AC_PREREQ([2.64])
 AC_INIT([harfbuzz],
-        [0.7.0],
+        [0.9.0],
         [http://bugs.freedesktop.org/enter_bug.cgi?product=harfbuzz],
         [harfbuzz],
         [http://harfbuzz.org/])
diff --git a/src/hb-ot-shape-complex-indic-private.hh b/src/hb-ot-shape-complex-indic-private.hh
index f06ebd0..64af0da 100644
--- a/src/hb-ot-shape-complex-indic-private.hh
+++ b/src/hb-ot-shape-complex-indic-private.hh
@@ -68,8 +68,8 @@ enum indic_position_t {
   POS_PRE_C,
   POS_BASE_C,
   POS_ABOVE_C,
-  POS_ABOVE_M,
   POS_BELOW_C,
+  POS_ABOVE_M,
   POS_BELOW_M,
   POS_POST_C,
   POS_POST_M,
commit 6a091df9b403b147ef78f3974610dedf4ce1e08a
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Fri May 11 21:42:27 2012 +0200

    [Indic] Disambiguate sub vs post vs above matras
    
    Bengali is at *just* above 5% now.

diff --git a/src/hb-ot-shape-complex-indic-private.hh b/src/hb-ot-shape-complex-indic-private.hh
index eaf1de8..f06ebd0 100644
--- a/src/hb-ot-shape-complex-indic-private.hh
+++ b/src/hb-ot-shape-complex-indic-private.hh
@@ -64,16 +64,15 @@ enum indic_category_t {
 /* Visual positions in a syllable from left to right. */
 enum indic_position_t {
   POS_RA_TO_BECOME_REPH,
-  POS_LEFT_MATRA,
-
+  POS_PRE_M,
   POS_PRE_C,
   POS_BASE_C,
   POS_ABOVE_C,
+  POS_ABOVE_M,
   POS_BELOW_C,
+  POS_BELOW_M,
   POS_POST_C,
-
-  POS_MATRAS,
-
+  POS_POST_M,
   POS_SMVD
 };
 
@@ -107,10 +106,10 @@ enum indic_syllabic_category_t {
 enum indic_matra_category_t {
   INDIC_MATRA_CATEGORY_NOT_APPLICABLE		= POS_BASE_C,
 
-  INDIC_MATRA_CATEGORY_LEFT			= POS_LEFT_MATRA,
-  INDIC_MATRA_CATEGORY_TOP			= POS_MATRAS,
-  INDIC_MATRA_CATEGORY_BOTTOM			= POS_MATRAS,
-  INDIC_MATRA_CATEGORY_RIGHT			= POS_MATRAS,
+  INDIC_MATRA_CATEGORY_LEFT			= POS_PRE_M,
+  INDIC_MATRA_CATEGORY_TOP			= POS_ABOVE_M,
+  INDIC_MATRA_CATEGORY_BOTTOM			= POS_BELOW_M,
+  INDIC_MATRA_CATEGORY_RIGHT			= POS_POST_M,
 
   /* We don't really care much about these since we decompose them
    * in the generic pre-shaping layer.  They will only be used if
@@ -121,13 +120,13 @@ enum indic_matra_category_t {
    * TODO: There are some split matras without Unicode decompositions.
    * We have to figure out what to do with them.
    */
-  INDIC_MATRA_CATEGORY_BOTTOM_AND_RIGHT		= POS_MATRAS,
-  INDIC_MATRA_CATEGORY_LEFT_AND_RIGHT		= POS_LEFT_MATRA,
-  INDIC_MATRA_CATEGORY_TOP_AND_BOTTOM		= POS_MATRAS,
-  INDIC_MATRA_CATEGORY_TOP_AND_BOTTOM_AND_RIGHT	= POS_MATRAS,
-  INDIC_MATRA_CATEGORY_TOP_AND_LEFT		= POS_LEFT_MATRA,
-  INDIC_MATRA_CATEGORY_TOP_AND_LEFT_AND_RIGHT	= POS_LEFT_MATRA,
-  INDIC_MATRA_CATEGORY_TOP_AND_RIGHT		= POS_MATRAS,
+  INDIC_MATRA_CATEGORY_BOTTOM_AND_RIGHT		= POS_POST_M,
+  INDIC_MATRA_CATEGORY_LEFT_AND_RIGHT		= POS_PRE_M,
+  INDIC_MATRA_CATEGORY_TOP_AND_BOTTOM		= POS_BELOW_M,
+  INDIC_MATRA_CATEGORY_TOP_AND_BOTTOM_AND_RIGHT	= POS_POST_M,
+  INDIC_MATRA_CATEGORY_TOP_AND_LEFT		= POS_PRE_M,
+  INDIC_MATRA_CATEGORY_TOP_AND_LEFT_AND_RIGHT	= POS_PRE_M,
+  INDIC_MATRA_CATEGORY_TOP_AND_RIGHT		= POS_POST_M,
 
   INDIC_MATRA_CATEGORY_INVISIBLE		= INDIC_MATRA_CATEGORY_NOT_APPLICABLE,
   INDIC_MATRA_CATEGORY_OVERSTRUCK		= INDIC_MATRA_CATEGORY_NOT_APPLICABLE,
diff --git a/src/hb-ot-shape-complex-indic.cc b/src/hb-ot-shape-complex-indic.cc
index 89108bf..93b490e 100644
--- a/src/hb-ot-shape-complex-indic.cc
+++ b/src/hb-ot-shape-complex-indic.cc
@@ -410,9 +410,9 @@ initial_reordering_syllable (const hb_ot_map_t *map, hb_buffer_t *buffer, hb_mas
     for (unsigned int i = start + 1; i < end; i++)
       if ((FLAG (info[i].indic_category()) & (FLAG (OT_ZWNJ) | FLAG (OT_ZWJ) | FLAG (OT_N) | FLAG (OT_H)))) {
 	info[i].indic_position() = info[i - 1].indic_position();
-	if (info[i].indic_category() == OT_H && info[i].indic_position() == POS_LEFT_MATRA)
+	if (info[i].indic_category() == OT_H && info[i].indic_position() == POS_PRE_M)
 	  for (unsigned int j = i; j > start; j--)
-	    if (info[j - 1].indic_position() != POS_LEFT_MATRA) {
+	    if (info[j - 1].indic_position() != POS_PRE_M) {
 	      info[i].indic_position() = info[j - 1].indic_position();
 	      break;
 	    }
@@ -573,14 +573,14 @@ final_reordering_syllable (hb_buffer_t *buffer, hb_mask_t *mask_array,
     /* If we found no Halant we are done.  Otherwise only proceed if the Halant does
      * not belong to the Matra itself! */
     if (info[new_matra_pos].indic_category() == OT_H &&
-	info[new_matra_pos].indic_position() != POS_LEFT_MATRA) {
+	info[new_matra_pos].indic_position() != POS_PRE_M) {
       /* -> If ZWJ or ZWNJ follow this halant, position is moved after it. */
       if (new_matra_pos + 1 < end && is_joiner (info[new_matra_pos + 1]))
 	new_matra_pos++;
 
       /* Now go see if there's actually any matras... */
       for (unsigned int i = new_matra_pos; i > start; i--)
-	if (info[i - 1].indic_position () == POS_LEFT_MATRA)
+	if (info[i - 1].indic_position () == POS_PRE_M)
 	{
 	  unsigned int old_matra_pos = i - 1;
 	  hb_glyph_info_t matra = info[old_matra_pos];
@@ -703,7 +703,7 @@ final_reordering_syllable (hb_buffer_t *buffer, hb_mask_t *mask_array,
     {
       new_reph_pos = base;
       while (new_reph_pos < end &&
-	     !( FLAG (info[new_reph_pos + 1].indic_position()) & (FLAG (POS_MATRAS) | FLAG (POS_SMVD))))
+	     !( FLAG (info[new_reph_pos + 1].indic_position()) & (FLAG (POS_POST_C) | FLAG (POS_POST_M) | FLAG (POS_SMVD))))
 	new_reph_pos++;
       if (new_reph_pos < end)
         goto reph_move;
@@ -779,7 +779,7 @@ final_reordering_syllable (hb_buffer_t *buffer, hb_mask_t *mask_array,
 
 
   /* Apply 'init' to the Left Matra if it's a word start. */
-  if (info[start].indic_position () == POS_LEFT_MATRA &&
+  if (info[start].indic_position () == POS_PRE_M &&
       (!start ||
        !(FLAG (_hb_glyph_info_get_general_category (&buffer->info[start - 1])) &
 	 (FLAG (HB_UNICODE_GENERAL_CATEGORY_LOWERCASE_LETTER) |
commit 9d0d319a4a7e85d922e58fade0f40caae1c9f109
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Fri May 11 21:36:32 2012 +0200

    [Indic] Position Bengali Reph before matras

diff --git a/src/hb-ot-shape-complex-indic.cc b/src/hb-ot-shape-complex-indic.cc
index bc141a9..89108bf 100644
--- a/src/hb-ot-shape-complex-indic.cc
+++ b/src/hb-ot-shape-complex-indic.cc
@@ -565,29 +565,31 @@ final_reordering_syllable (hb_buffer_t *buffer, hb_mask_t *mask_array,
    *     halant, position is moved after it.
    */
 
-  unsigned int new_matra_pos = base - 1;
-  while (new_matra_pos > start &&
-	 !(FLAG (info[new_matra_pos].indic_category()) & (FLAG (OT_M) | FLAG (OT_H))))
-    new_matra_pos--;
-  /* If we found no Halant we are done.  Otherwise only proceed if the Halant does
-   * not belong to the Matra itself! */
-  if (info[new_matra_pos].indic_category() == OT_H &&
-      info[new_matra_pos].indic_position() != POS_LEFT_MATRA) {
-    /* -> If ZWJ or ZWNJ follow this halant, position is moved after it. */
-    if (new_matra_pos + 1 < end && is_joiner (info[new_matra_pos + 1]))
-      new_matra_pos++;
-
-    /* Now go see if there's actually any matras... */
-    for (unsigned int i = new_matra_pos; i > start; i--)
-      if (info[i - 1].indic_position () == POS_LEFT_MATRA)
-      {
-	unsigned int old_matra_pos = i - 1;
-	hb_glyph_info_t matra = info[old_matra_pos];
-	memmove (&info[old_matra_pos], &info[old_matra_pos + 1], (new_matra_pos - old_matra_pos) * sizeof (info[0]));
-	info[new_matra_pos] = matra;
-	start_of_last_cluster = MIN (new_matra_pos, start_of_last_cluster);
-	new_matra_pos--;
-      }
+  {
+    unsigned int new_matra_pos = base - 1;
+    while (new_matra_pos > start &&
+	   !(FLAG (info[new_matra_pos].indic_category()) & (FLAG (OT_M) | FLAG (OT_H))))
+      new_matra_pos--;
+    /* If we found no Halant we are done.  Otherwise only proceed if the Halant does
+     * not belong to the Matra itself! */
+    if (info[new_matra_pos].indic_category() == OT_H &&
+	info[new_matra_pos].indic_position() != POS_LEFT_MATRA) {
+      /* -> If ZWJ or ZWNJ follow this halant, position is moved after it. */
+      if (new_matra_pos + 1 < end && is_joiner (info[new_matra_pos + 1]))
+	new_matra_pos++;
+
+      /* Now go see if there's actually any matras... */
+      for (unsigned int i = new_matra_pos; i > start; i--)
+	if (info[i - 1].indic_position () == POS_LEFT_MATRA)
+	{
+	  unsigned int old_matra_pos = i - 1;
+	  hb_glyph_info_t matra = info[old_matra_pos];
+	  memmove (&info[old_matra_pos], &info[old_matra_pos + 1], (new_matra_pos - old_matra_pos) * sizeof (info[0]));
+	  info[new_matra_pos] = matra;
+	  start_of_last_cluster = MIN (new_matra_pos, start_of_last_cluster);
+	  new_matra_pos--;
+	}
+    }
   }
 
 
@@ -650,9 +652,9 @@ final_reordering_syllable (hb_buffer_t *buffer, hb_mask_t *mask_array,
      *          proceed to step 5.
      */
     reph_step_1:
+    if (reph_pos == REPH_AFTER_POSTSCRIPT)
     {
-      if (reph_pos == REPH_AFTER_POSTSCRIPT)
-	goto reph_step_5;
+      goto reph_step_5;
     }
 
     /*       2. If the reph repositioning class is not after post-base: target
@@ -685,6 +687,7 @@ final_reordering_syllable (hb_buffer_t *buffer, hb_mask_t *mask_array,
      *          consonant that is not a potential pre-base reordering Ra.
      */
     reph_step_3:
+    if (reph_pos == REPH_AFTER_MAIN)
     {
       /* XXX */
     }
@@ -695,8 +698,15 @@ final_reordering_syllable (hb_buffer_t *buffer, hb_mask_t *mask_array,
      *          first matra, syllable modifier sign or vedic sign.
      */
     reph_step_4:
+    /* This is our take on what step 4 is trying to say (and failing, BADLY). */
+    if (reph_pos == REPH_AFTER_SUBSCRIPT)
     {
-      /* XXX */
+      new_reph_pos = base;
+      while (new_reph_pos < end &&
+	     !( FLAG (info[new_reph_pos + 1].indic_position()) & (FLAG (POS_MATRAS) | FLAG (POS_SMVD))))
+	new_reph_pos++;
+      if (new_reph_pos < end)
+        goto reph_move;
     }
 
     /*       5. If no consonant is found in steps 3 or 4, move reph to a position
commit f89367251109af235f4f0446c13c261a5a4a6f72
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Fri May 11 21:10:03 2012 +0200

    [Indic] Start categorizing Reph per script

diff --git a/src/hb-ot-shape-complex-indic.cc b/src/hb-ot-shape-complex-indic.cc
index 20e7ed7..bc141a9 100644
--- a/src/hb-ot-shape-complex-indic.cc
+++ b/src/hb-ot-shape-complex-indic.cc
@@ -610,12 +610,41 @@ final_reordering_syllable (hb_buffer_t *buffer, hb_mask_t *mask_array,
       unsigned int new_reph_pos;
 
      enum reph_position_t {
-       REPH_AFTER_MAIN, /* Malayalam, Oriya */
-       REPH_BEFORE_SUBSCRIPT, /* Gurmukhi  */
-       REPH_AFTER_SUBSCRIPT, /* Bengali */
-       REPH_BEFORE_POSTSCRIPT, /* Devanagari, Gujarati  */
-       REPH_AFTER_POSTSCRIPT, /* Kannada, Tamil, Telugu  */
-     } reph_pos = REPH_BEFORE_POSTSCRIPT; /* XXX */ /* XXX Figure out old behavior too */
+       REPH_AFTER_MAIN,
+       REPH_BEFORE_SUBSCRIPT,
+       REPH_AFTER_SUBSCRIPT,
+       REPH_BEFORE_POSTSCRIPT,
+       REPH_AFTER_POSTSCRIPT,
+     } reph_pos;
+
+     /* XXX Figure out old behavior too */
+     switch (buffer->props.script)
+     {
+       case HB_SCRIPT_MALAYALAM:
+       case HB_SCRIPT_ORIYA:
+	 reph_pos = REPH_AFTER_MAIN;
+	 break;
+
+       case HB_SCRIPT_GURMUKHI:
+	 reph_pos = REPH_BEFORE_SUBSCRIPT;
+	 break;
+
+       case HB_SCRIPT_BENGALI:
+	 reph_pos = REPH_AFTER_SUBSCRIPT;
+	 break;
+
+       default:
+       case HB_SCRIPT_DEVANAGARI:
+       case HB_SCRIPT_GUJARATI:
+	 reph_pos = REPH_BEFORE_POSTSCRIPT;
+	 break;
+
+       case HB_SCRIPT_KANNADA:
+       case HB_SCRIPT_TAMIL:
+       case HB_SCRIPT_TELUGU:
+	 reph_pos = REPH_AFTER_POSTSCRIPT;
+	 break;
+     }
 
     /*       1. If reph should be positioned after post-base consonant forms,
      *          proceed to step 5.
commit a913b024d84973556094fd64ce5f0b7106fcc3b5
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Fri May 11 20:59:26 2012 +0200

    [Indic] Apply 'init' feature for Bengali
    
    Error down from 20% to 7%.

diff --git a/src/hb-ot-shape-complex-indic.cc b/src/hb-ot-shape-complex-indic.cc
index 7e3851d..20e7ed7 100644
--- a/src/hb-ot-shape-complex-indic.cc
+++ b/src/hb-ot-shape-complex-indic.cc
@@ -25,6 +25,7 @@
  */
 
 #include "hb-ot-shape-complex-indic-private.hh"
+#include "hb-ot-shape-private.hh"
 
 static const struct indic_options_t
 {
@@ -738,6 +739,20 @@ final_reordering_syllable (hb_buffer_t *buffer, hb_mask_t *mask_array,
 
 
 
+  /* Apply 'init' to the Left Matra if it's a word start. */
+  if (info[start].indic_position () == POS_LEFT_MATRA &&
+      (!start ||
+       !(FLAG (_hb_glyph_info_get_general_category (&buffer->info[start - 1])) &
+	 (FLAG (HB_UNICODE_GENERAL_CATEGORY_LOWERCASE_LETTER) |
+	  FLAG (HB_UNICODE_GENERAL_CATEGORY_MODIFIER_LETTER) |
+	  FLAG (HB_UNICODE_GENERAL_CATEGORY_OTHER_LETTER) |
+	  FLAG (HB_UNICODE_GENERAL_CATEGORY_TITLECASE_LETTER) |
+	  FLAG (HB_UNICODE_GENERAL_CATEGORY_UPPERCASE_LETTER) |
+	  FLAG (HB_UNICODE_GENERAL_CATEGORY_SPACING_MARK) |
+	  FLAG (HB_UNICODE_GENERAL_CATEGORY_ENCLOSING_MARK) |
+	  FLAG (HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK)))))
+    info[start].mask |= mask_array[INIT];
+
 
 
   /* Finish off the clusters and go home! */
commit eed903b1644e087178438959664a6a57bebc398b
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Fri May 11 20:50:53 2012 +0200

    [Indic] Refactor for the arrival of 'init' feature
    
    Yep, on Bengali now!

diff --git a/src/hb-ot-shape-complex-indic.cc b/src/hb-ot-shape-complex-indic.cc
index c063e5b..7e3851d 100644
--- a/src/hb-ot-shape-complex-indic.cc
+++ b/src/hb-ot-shape-complex-indic.cc
@@ -85,10 +85,13 @@ is_consonant (const hb_glyph_info_t &info)
   return !!(FLAG (info.indic_category()) & (FLAG (OT_C) | FLAG (OT_Ra) | FLAG (OT_V) | FLAG (OT_NBSP) | FLAG (OT_DOTTEDCIRCLE)));
 }
 
-static const struct {
+struct feature_list_t {
   hb_tag_t tag;
   hb_bool_t is_global;
-} indic_basic_features[] =
+};
+
+static const feature_list_t
+indic_basic_features[] =
 {
   {HB_TAG('n','u','k','t'), true},
   {HB_TAG('a','k','h','n'), false},
@@ -116,17 +119,24 @@ enum {
   CJCT
 };
 
-static const hb_tag_t indic_other_features[] =
+static const feature_list_t
+indic_other_features[] =
 {
-  HB_TAG('p','r','e','s'),
-  HB_TAG('a','b','v','s'),
-  HB_TAG('b','l','w','s'),
-  HB_TAG('p','s','t','s'),
-  HB_TAG('h','a','l','n'),
-
-  HB_TAG('d','i','s','t'),
-  HB_TAG('a','b','v','m'),
-  HB_TAG('b','l','w','m'),
+  {HB_TAG('i','n','i','t'), false},
+  {HB_TAG('p','r','e','s'), true},
+  {HB_TAG('a','b','v','s'), true},
+  {HB_TAG('b','l','w','s'), true},
+  {HB_TAG('p','s','t','s'), true},
+  {HB_TAG('h','a','l','n'), true},
+
+  {HB_TAG('d','i','s','t'), true},
+  {HB_TAG('a','b','v','m'), true},
+  {HB_TAG('b','l','w','m'), true},
+};
+
+/* Same order as the indic_other_features array */
+enum {
+  INIT
 };
 
 
@@ -159,7 +169,7 @@ _hb_ot_shape_complex_collect_features_indic (hb_ot_map_builder_t *map, const hb_
   map->add_gsub_pause (final_reordering, NULL);
 
   for (unsigned int i = 0; i < ARRAY_LENGTH (indic_other_features); i++) {
-    map->add_bool_feature (indic_other_features[i], true);
+    map->add_bool_feature (indic_other_features[i].tag, indic_other_features[i].is_global);
     map->add_gsub_pause (NULL, NULL);
   }
 }
@@ -514,7 +524,7 @@ initial_reordering (const hb_ot_map_t *map,
 }
 
 static void
-final_reordering_syllable (hb_buffer_t *buffer,
+final_reordering_syllable (hb_buffer_t *buffer, hb_mask_t *mask_array,
 			   unsigned int start, unsigned int end)
 {
   hb_glyph_info_t *info = buffer->info;
@@ -724,6 +734,11 @@ final_reordering_syllable (hb_buffer_t *buffer,
    *          consonant.
    */
 
+  /* TODO */
+
+
+
+
 
   /* Finish off the clusters and go home! */
 
@@ -756,16 +771,21 @@ final_reordering (const hb_ot_map_t *map,
   unsigned int count = buffer->len;
   if (!count) return;
 
+  hb_mask_t mask_array[ARRAY_LENGTH (indic_other_features)] = {0};
+  unsigned int num_masks = ARRAY_LENGTH (indic_other_features);
+  for (unsigned int i = 0; i < num_masks; i++)
+    mask_array[i] = map->get_1_mask (indic_other_features[i].tag);
+
   hb_glyph_info_t *info = buffer->info;
   unsigned int last = 0;
   unsigned int last_syllable = info[0].syllable();
   for (unsigned int i = 1; i < count; i++)
     if (last_syllable != info[i].syllable()) {
-      final_reordering_syllable (buffer, last, i);
+      final_reordering_syllable (buffer, mask_array, last, i);
       last = i;
       last_syllable = info[last].syllable();
     }
-  final_reordering_syllable (buffer, last, count);
+  final_reordering_syllable (buffer, mask_array, last, count);
 
   HB_BUFFER_DEALLOCATE_VAR (buffer, indic_category);
   HB_BUFFER_DEALLOCATE_VAR (buffer, indic_position);
commit 18c06e189bd078affbb84c3bb5bb80687a227c5e
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Fri May 11 20:02:14 2012 +0200

    [Indic] Add Uniscribe bug feature for dotted circle
    
    For dotted-circle independent clusters, Uniscribe does no Reph shaping
    for the exact sequence Ra+Halant+25CC.  Which also is the only possible
    sequence with 25CC at the end.

diff --git a/src/hb-ot-shape-complex-indic-machine.rl b/src/hb-ot-shape-complex-indic-machine.rl
index d8b3de5..207a88f 100644
--- a/src/hb-ot-shape-complex-indic-machine.rl
+++ b/src/hb-ot-shape-complex-indic-machine.rl
@@ -53,17 +53,19 @@ SM   = 9;
 VD   = 10;
 A    = 11;
 NBSP = 12;
+DOTTEDCIRCLE = 13;
 
 c = C | Ra;
 n = N N?;
 z = ZWJ|ZWNJ;
 matra_group = M N? H?;
 syllable_tail = SM? (VD VD?)?;
+place_holder = NBSP | DOTTEDCIRCLE;
 
 
 consonant_syllable =	(c.n? (H.z?|z.H))* c.n? A? (H.z? | matra_group*)? syllable_tail;
 vowel_syllable =	(Ra H)? V n? (z?.H.c | ZWJ.c)* matra_group* syllable_tail;
-standalone_cluster =	(Ra H)? NBSP n? (z? H c)* matra_group* syllable_tail;
+standalone_cluster =	(Ra H)? place_holder n? (z? H c)* matra_group* syllable_tail;
 other =			any;
 
 main := |*
diff --git a/src/hb-ot-shape-complex-indic-private.hh b/src/hb-ot-shape-complex-indic-private.hh
index 0d18536..eaf1de8 100644
--- a/src/hb-ot-shape-complex-indic-private.hh
+++ b/src/hb-ot-shape-complex-indic-private.hh
@@ -57,7 +57,8 @@ enum indic_category_t {
   OT_SM,
   OT_VD,
   OT_A,
-  OT_NBSP
+  OT_NBSP,
+  OT_DOTTEDCIRCLE /* Not in the spec, but special in Uniscribe. /Very very/ special! */
 };
 
 /* Visual positions in a syllable from left to right. */
diff --git a/src/hb-ot-shape-complex-indic.cc b/src/hb-ot-shape-complex-indic.cc
index d32f7df..c063e5b 100644
--- a/src/hb-ot-shape-complex-indic.cc
+++ b/src/hb-ot-shape-complex-indic.cc
@@ -79,10 +79,10 @@ is_consonant (const hb_glyph_info_t &info)
 {
   /* Note:
    *
-   * We treat Vowels and NBSP as if they were consonants.  This is safe because Vowels
+   * We treat Vowels and placeholders as if they were consonants.  This is safe because Vowels
    * cannot happen in a consonant syllable.  The plus side however is, we can call the
    * consonant syllable logic from the vowel syllable function and get it all right! */
-  return !!(FLAG (info.indic_category()) & (FLAG (OT_C) | FLAG (OT_Ra) | FLAG (OT_V) | FLAG (OT_NBSP)));
+  return !!(FLAG (info.indic_category()) & (FLAG (OT_C) | FLAG (OT_Ra) | FLAG (OT_V) | FLAG (OT_NBSP) | FLAG (OT_DOTTEDCIRCLE)));
 }
 
 static const struct {
@@ -213,7 +213,8 @@ _hb_ot_shape_complex_setup_masks_indic (hb_ot_map_t *map, hb_buffer_t *buffer, h
       info.indic_category() = OT_ZWNJ;
     else if (unlikely (info.codepoint == 0x200D))
       info.indic_category() = OT_ZWJ;
-
+    else if (unlikely (info.codepoint == 0x25CC))
+      info.indic_category() = OT_DOTTEDCIRCLE;
   }
 }
 
@@ -473,7 +474,18 @@ static void
 initial_reordering_standalone_cluster (const hb_ot_map_t *map, hb_buffer_t *buffer, hb_mask_t *mask_array,
 				       unsigned int start, unsigned int end)
 {
-  /* We made the vowels look like consonants.  So let's call the consonant logic! */
+  /* We treat NBSP/dotted-circle as if they are consonants, so we should just chain.
+   * Only if not in compatibility mode that is... */
+
+  if (options.uniscribe_bug_compatible)
+  {
+    /* For dotted-circle, this is what Uniscribe does:
+     * If dotted-circle is the last glyph, it just does nothing.
+     * Ie. It doesn't form Reph. */
+    if (buffer->info[end - 1].indic_category() == OT_DOTTEDCIRCLE)
+      return;
+  }
+
   initial_reordering_consonant_syllable (map, buffer, mask_array, start, end);
 }
 
commit 5b16de97bcc4b24da4c77ca6c1a42e814d8cdbd1
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Fri May 11 19:55:42 2012 +0200

    [Indic] Add tests for dottedcircle

diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-devanagari/misc/MANIFEST b/test/shaping/texts/in-tree/shaper-indic/indic/script-devanagari/misc/MANIFEST
index d361ae0..a68b307 100644
--- a/test/shaping/texts/in-tree/shaper-indic/indic/script-devanagari/misc/MANIFEST
+++ b/test/shaping/texts/in-tree/shaper-indic/indic/script-devanagari/misc/MANIFEST
@@ -1,3 +1,4 @@
+dottedcircle.txt
 joiners.txt
 misc.txt
 spec-deviations.txt
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-devanagari/misc/dottedcircle.txt b/test/shaping/texts/in-tree/shaper-indic/indic/script-devanagari/misc/dottedcircle.txt
new file mode 100644
index 0000000..52eeef5
--- /dev/null
+++ b/test/shaping/texts/in-tree/shaper-indic/indic/script-devanagari/misc/dottedcircle.txt
@@ -0,0 +1,7 @@
+र्◌
+र्◌्च
+र्◌्च्छे
+र्◌ि
+र्◌्
+र्◌़
+◌्च्छे
commit 0831061efb78983b9c6e1e72574c977e56383c08
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Fri May 11 19:07:58 2012 +0200

    [Indic] Refactoring

diff --git a/src/hb-ot-shape-complex-indic.cc b/src/hb-ot-shape-complex-indic.cc
index 9732c76..d32f7df 100644
--- a/src/hb-ot-shape-complex-indic.cc
+++ b/src/hb-ot-shape-complex-indic.cc
@@ -230,6 +230,10 @@ compare_indic_order (const hb_glyph_info_t *pa, const hb_glyph_info_t *pb)
  * https://www.microsoft.com/typography/otfntdev/devanot/shaping.aspx */
 
 static void
+initial_reordering_syllable (const hb_ot_map_t *map, hb_buffer_t *buffer, hb_mask_t *mask_array,
+			     unsigned int start, unsigned int end, unsigned int base);
+
+static void
 initial_reordering_consonant_syllable (const hb_ot_map_t *map, hb_buffer_t *buffer, hb_mask_t *mask_array,
 				       unsigned int start, unsigned int end)
 {
@@ -301,14 +305,15 @@ initial_reordering_consonant_syllable (const hb_ot_map_t *map, hb_buffer_t *buff
       base = start; /* Just in case... */
   }
 
-  /* -> If the syllable starts with Ra + Halant (in a script that has Reph)
-   *    and has more than one consonant, Ra is excluded from candidates for
-   *    base consonants. */
-  if (has_reph && base == start) {
-    /* Have no other consonant, so Reph is not formed and Ra becomes base. */
-    has_reph = false;
-  }
+  /* Continue reading below ------v */
+  initial_reordering_syllable (map, buffer, mask_array, start, end, base);
+}
 
+static void
+initial_reordering_syllable (const hb_ot_map_t *map, hb_buffer_t *buffer, hb_mask_t *mask_array,
+			     unsigned int start, unsigned int end, unsigned int base)
+{
+  hb_glyph_info_t *info = buffer->info;
 
   /* 2. Decompose and reorder Matras:
    *
@@ -349,7 +354,12 @@ initial_reordering_consonant_syllable (const hb_ot_map_t *map, hb_buffer_t *buff
   info[base].indic_position() = POS_BASE_C;
 
   /* Handle beginning Ra */
-  if (has_reph)
+  if (mask_array[RPHF] &&
+      start != base &&
+      start + 3 <= end &&
+      info[start].indic_category() == OT_Ra &&
+      info[start + 1].indic_category() == OT_H &&
+      !is_joiner (info[start + 2]))
     info[start].indic_position() = POS_RA_TO_BECOME_REPH;
 
   /* For old-style Indic script tags, move the first post-base Halant after
commit 7ea58db311bfb0d8f804d1e9f4a1f004bd45075a
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Fri May 11 18:58:57 2012 +0200

    Minor

diff --git a/src/hb-ot-shape-complex-indic.cc b/src/hb-ot-shape-complex-indic.cc
index a3a75e4..9732c76 100644
--- a/src/hb-ot-shape-complex-indic.cc
+++ b/src/hb-ot-shape-complex-indic.cc
@@ -226,15 +226,15 @@ compare_indic_order (const hb_glyph_info_t *pa, const hb_glyph_info_t *pb)
   return a < b ? -1 : a == b ? 0 : +1;
 }
 
+/* Rules from:
+ * https://www.microsoft.com/typography/otfntdev/devanot/shaping.aspx */
+
 static void
 initial_reordering_consonant_syllable (const hb_ot_map_t *map, hb_buffer_t *buffer, hb_mask_t *mask_array,
 				       unsigned int start, unsigned int end)
 {
   hb_glyph_info_t *info = buffer->info;
 
-  /* Comments from:
-   * https://www.microsoft.com/typography/otfntdev/devanot/shaping.aspx */
-
   /* 1. Find base consonant:
    *
    * The shaping engine finds the base consonant of the syllable, using the
commit 9c09928989316e2befe00d52ed66e055637ccd36
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Fri May 11 18:46:35 2012 +0200

    [Indic] Allow multiple Consonants in Vowel/NBSP syllables
    
    Uniscribe allows multiple Halant+Consonant after a Vowel.
    Tests:
    ↦       * U+0905,U+094D,U+092B,U+094D,930,94d,930

diff --git a/src/hb-ot-shape-complex-indic-machine.rl b/src/hb-ot-shape-complex-indic-machine.rl
index dedcc5c..d8b3de5 100644
--- a/src/hb-ot-shape-complex-indic-machine.rl
+++ b/src/hb-ot-shape-complex-indic-machine.rl
@@ -62,8 +62,8 @@ syllable_tail = SM? (VD VD?)?;
 
 
 consonant_syllable =	(c.n? (H.z?|z.H))* c.n? A? (H.z? | matra_group*)? syllable_tail;
-vowel_syllable =	(Ra H)? V n? (z?.H.c | ZWJ.c)? matra_group* syllable_tail;
-standalone_cluster =	(Ra H)? NBSP n? (z? H c)? matra_group* syllable_tail;
+vowel_syllable =	(Ra H)? V n? (z?.H.c | ZWJ.c)* matra_group* syllable_tail;
+standalone_cluster =	(Ra H)? NBSP n? (z? H c)* matra_group* syllable_tail;
 other =			any;
 
 main := |*
@@ -78,7 +78,7 @@ main := |*
 
 #define process_syllable(func) \
   HB_STMT_START { \
-    /* printf ("syllable %d..%d %s\n", last, p+1, #func); */ \
+    /* fprintf (stderr, "syllable %d..%d %s\n", last, p+1, #func); */ \
     for (unsigned int i = last; i < p+1; i++) \
       info[i].syllable() = syllable_serial; \
     PASTE (initial_reordering_, func) (map, buffer, mask_array, last, p+1); \
commit 8c0aa486f31e9b6cbb31ce295573b53b0a214124
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Fri May 11 18:13:42 2012 +0200

    [Indic] Allow two Nuktas per consonant
    
    Uniscribe allows up to two nuktas per consonant and one per matra. It does so
    indepent of whether the consonant already has a nukta in it.  Tests:
    
            * U+0916,U+093C,U+0941
            * U+0959,U+093C,U+0941
            * U+0916,U+093C,U+093C,U+0941
            * U+0959,U+093C,U+093C,U+0941
            * U+0916,U+093C,U+093C,U+093C,U+0941
            * U+0959,U+093C,U+093C,U+093C,U+0941
            * 915,93c,93c,,94d,U+0916,U+093C,U+093C,U+093e,93c,93c

diff --git a/src/hb-ot-shape-complex-indic-machine.rl b/src/hb-ot-shape-complex-indic-machine.rl
index b26e2a7..dedcc5c 100644
--- a/src/hb-ot-shape-complex-indic-machine.rl
+++ b/src/hb-ot-shape-complex-indic-machine.rl
@@ -55,14 +55,15 @@ A    = 11;
 NBSP = 12;
 
 c = C | Ra;
+n = N N?;
 z = ZWJ|ZWNJ;
 matra_group = M N? H?;
 syllable_tail = SM? (VD VD?)?;
 
 
-consonant_syllable =	(c.N? (H.z?|z.H))* c.N? A? (H.z? | matra_group*)? syllable_tail;
-vowel_syllable =	(Ra H)? V N? (z?.H.c | ZWJ.c)? matra_group* syllable_tail;
-standalone_cluster =	(Ra H)? NBSP N? (z? H c)? matra_group* syllable_tail;
+consonant_syllable =	(c.n? (H.z?|z.H))* c.n? A? (H.z? | matra_group*)? syllable_tail;
+vowel_syllable =	(Ra H)? V n? (z?.H.c | ZWJ.c)? matra_group* syllable_tail;
+standalone_cluster =	(Ra H)? NBSP n? (z? H c)? matra_group* syllable_tail;
 other =			any;
 
 main := |*
commit 3399a06e7033651ee926448737bdb18e553c1796
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Fri May 11 17:54:26 2012 +0200

    [Indic] Fix U+0952 and similar classification to match Uniscribe
    
    See comments.

diff --git a/src/hb-ot-shape-complex-indic-machine.rl b/src/hb-ot-shape-complex-indic-machine.rl
index bf77b02..b26e2a7 100644
--- a/src/hb-ot-shape-complex-indic-machine.rl
+++ b/src/hb-ot-shape-complex-indic-machine.rl
@@ -77,6 +77,7 @@ main := |*
 
 #define process_syllable(func) \
   HB_STMT_START { \
+    /* printf ("syllable %d..%d %s\n", last, p+1, #func); */ \
     for (unsigned int i = last; i < p+1; i++) \
       info[i].syllable() = syllable_serial; \
     PASTE (initial_reordering_, func) (map, buffer, mask_array, last, p+1); \
diff --git a/src/hb-ot-shape-complex-indic.cc b/src/hb-ot-shape-complex-indic.cc
index ec60738..a3a75e4 100644
--- a/src/hb-ot-shape-complex-indic.cc
+++ b/src/hb-ot-shape-complex-indic.cc
@@ -191,6 +191,17 @@ _hb_ot_shape_complex_setup_masks_indic (hb_ot_map_t *map, hb_buffer_t *buffer, h
     info.indic_category() = type & 0x0F;
     info.indic_position() = type >> 4;
 
+    /* The spec says U+0952 is OT_A.  However, testing shows that Uniscribe
+     * treats U+0951..U+0952 all as OT_VD.
+     * TESTS:
+     * U+092E,U+0947,U+0952
+     * U+092E,U+0952,U+0947
+     * U+092E,U+0947,U+0951
+     * U+092E,U+0951,U+0947
+     * */
+    if (unlikely (hb_in_range<hb_codepoint_t> (info.codepoint, 0x0951, 0x0954)))
+      info.indic_category() = OT_VD;
+
     if (info.indic_category() == OT_C) {
       info.indic_position() = consonant_position (info.codepoint);
       if (is_ra (info.codepoint))
@@ -203,11 +214,6 @@ _hb_ot_shape_complex_setup_masks_indic (hb_ot_map_t *map, hb_buffer_t *buffer, h
     else if (unlikely (info.codepoint == 0x200D))
       info.indic_category() = OT_ZWJ;
 
-    /* The spec only suggests this for U+0952, but we do more. */
-    if (unlikely (hb_in_range<hb_codepoint_t> (info.codepoint, 0x0951, 0x0954))) {
-      info.indic_category() = OT_A;
-      info.indic_position() = POS_SMVD;
-    }
   }
 }
 
commit 11aa3ef18dbc6ac9561bd119f5ca2c1aa1209c3a
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Fri May 11 17:30:48 2012 +0200

    [Indic] Treat U+0951..U+0954 all similar to U+0952

diff --git a/src/hb-ot-shape-complex-indic.cc b/src/hb-ot-shape-complex-indic.cc
index 089df06..ec60738 100644
--- a/src/hb-ot-shape-complex-indic.cc
+++ b/src/hb-ot-shape-complex-indic.cc
@@ -203,7 +203,8 @@ _hb_ot_shape_complex_setup_masks_indic (hb_ot_map_t *map, hb_buffer_t *buffer, h
     else if (unlikely (info.codepoint == 0x200D))
       info.indic_category() = OT_ZWJ;
 
-    if (unlikely (info.codepoint == 0x0952)) {
+    /* The spec only suggests this for U+0952, but we do more. */
+    if (unlikely (hb_in_range<hb_codepoint_t> (info.codepoint, 0x0951, 0x0954))) {
       info.indic_category() = OT_A;
       info.indic_position() = POS_SMVD;
     }
commit 5f131d3226131df440d7f36655e57f6effcae204
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Fri May 11 17:29:40 2012 +0200

    [GSUB/GPOS/Indic] Apply GSUB/GPOS within syllables only
    
    This does not apply to the context matchings.
    
    This regresses tests right now.  And we are not sure whether this is
    the right thing to do for GPOS.  But we'll figure out.

diff --git a/src/hb-ot-layout-gsubgpos-private.hh b/src/hb-ot-layout-gsubgpos-private.hh
index 77dc704..96e6ddb 100644
--- a/src/hb-ot-layout-gsubgpos-private.hh
+++ b/src/hb-ot-layout-gsubgpos-private.hh
@@ -135,12 +135,14 @@ struct hb_apply_context_t
     inline mark_skipping_forward_iterator_t (hb_apply_context_t *c_,
 					     unsigned int start_index_,
 					     unsigned int num_items_,
-					     hb_mask_t mask_ = 0)
+					     hb_mask_t mask_ = 0,
+					     bool match_syllable_ = true)
     {
       c = c_;
       idx = start_index_;
       num_items = num_items_;
       mask = mask_ ? mask_ : c->lookup_mask;
+      syllable = match_syllable_ ? c->buffer->info[c->buffer->idx].syllable () : 0;
       end = MIN (c->buffer->len, c->buffer->idx + c->context_length);
     }
     inline bool has_no_chance (void) const
@@ -158,7 +160,7 @@ struct hb_apply_context_t
 	idx++;
       } while (_hb_ot_layout_skip_mark (c->face, &c->buffer->info[idx], lookup_props, property_out));
       num_items--;
-      return !!(c->buffer->info[idx].mask & mask);
+      return (c->buffer->info[idx].mask & mask) && (!syllable || syllable == c->buffer->info[idx].syllable ());
     }
     inline bool next (unsigned int *property_out = NULL)
     {
@@ -170,6 +172,7 @@ struct hb_apply_context_t
     hb_apply_context_t *c;
     unsigned int num_items;
     hb_mask_t mask;
+    uint8_t syllable;
     unsigned int end;
   };
 
@@ -178,12 +181,14 @@ struct hb_apply_context_t
     inline mark_skipping_backward_iterator_t (hb_apply_context_t *c_,
 					      unsigned int start_index_,
 					      unsigned int num_items_,
-					      hb_mask_t mask_ = 0)
+					      hb_mask_t mask_ = 0,
+					     bool match_syllable_ = true)
     {
       c = c_;
       idx = start_index_;
       num_items = num_items_;
       mask = mask_ ? mask_ : c->lookup_mask;
+      syllable = match_syllable_ ? c->buffer->info[c->buffer->idx].syllable () : 0;
     }
     inline bool has_no_chance (void) const
     {
@@ -200,7 +205,7 @@ struct hb_apply_context_t
 	idx--;
       } while (_hb_ot_layout_skip_mark (c->face, &c->buffer->out_info[idx], lookup_props, property_out));
       num_items--;
-      return !!(c->buffer->out_info[idx].mask & mask);
+      return (c->buffer->out_info[idx].mask & mask) && (!syllable || syllable == c->buffer->out_info[idx].syllable ());
     }
     inline bool prev (unsigned int *property_out = NULL)
     {
@@ -212,6 +217,7 @@ struct hb_apply_context_t
     hb_apply_context_t *c;
     unsigned int num_items;
     hb_mask_t mask;
+    uint8_t syllable;
   };
 
   inline bool should_mark_skip_current_glyph (void) const
@@ -341,7 +347,7 @@ static inline bool match_backtrack (hb_apply_context_t *c,
 				    match_func_t match_func,
 				    const void *match_data)
 {
-  hb_apply_context_t::mark_skipping_backward_iterator_t skippy_iter (c, c->buffer->backtrack_len (), count, (hb_mask_t) -1);
+  hb_apply_context_t::mark_skipping_backward_iterator_t skippy_iter (c, c->buffer->backtrack_len (), count, (hb_mask_t) -1, false);
   if (skippy_iter.has_no_chance ())
     return false;
 
@@ -364,7 +370,7 @@ static inline bool match_lookahead (hb_apply_context_t *c,
 				    const void *match_data,
 				    unsigned int offset)
 {
-  hb_apply_context_t::mark_skipping_forward_iterator_t skippy_iter (c, c->buffer->idx + offset - 1, count, (hb_mask_t) -1);
+  hb_apply_context_t::mark_skipping_forward_iterator_t skippy_iter (c, c->buffer->idx + offset - 1, count, (hb_mask_t) -1, false);
   if (skippy_iter.has_no_chance ())
     return false;
 
commit 8fd83aaf6e50c2c25002c51fee26d82847a61769
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Fri May 11 17:18:37 2012 +0200

    [GSUB/GPOS] Fix wrong buffer access in backward skippy mask matching

diff --git a/src/hb-ot-layout-gsubgpos-private.hh b/src/hb-ot-layout-gsubgpos-private.hh
index 03a561a..77dc704 100644
--- a/src/hb-ot-layout-gsubgpos-private.hh
+++ b/src/hb-ot-layout-gsubgpos-private.hh
@@ -200,7 +200,7 @@ struct hb_apply_context_t
 	idx--;
       } while (_hb_ot_layout_skip_mark (c->face, &c->buffer->out_info[idx], lookup_props, property_out));
       num_items--;
-      return !!(c->buffer->info[idx].mask & mask);
+      return !!(c->buffer->out_info[idx].mask & mask);
     }
     inline bool prev (unsigned int *property_out = NULL)
     {
commit ff24d1081af08a887895975285d7e38f5d07bc37
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Fri May 11 17:07:08 2012 +0200

    [Indic] Don't use syllable serial value 0

diff --git a/src/hb-ot-shape-complex-indic-machine.rl b/src/hb-ot-shape-complex-indic-machine.rl
index ddecfcc..bf77b02 100644
--- a/src/hb-ot-shape-complex-indic-machine.rl
+++ b/src/hb-ot-shape-complex-indic-machine.rl
@@ -82,6 +82,7 @@ main := |*
     PASTE (initial_reordering_, func) (map, buffer, mask_array, last, p+1); \
     last = p+1; \
     syllable_serial++; \
+    if (unlikely (!syllable_serial)) syllable_serial++; \
   } HB_STMT_END
 
 static void
@@ -99,7 +100,7 @@ find_syllables (const hb_ot_map_t *map, hb_buffer_t *buffer, hb_mask_t *mask_arr
   pe = eof = buffer->len;
 
   unsigned int last = 0;
-  uint8_t syllable_serial = 0;
+  uint8_t syllable_serial = 1;
   %%{
     write exec;
   }%%
commit 892eb7878238d810a2a70f9dadbf958207bfeaa1
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Fri May 11 16:54:40 2012 +0200

    [Indic] Implement Uniscribe Reph+Matra+Halant bug feature

diff --git a/src/hb-ot-shape-complex-indic.cc b/src/hb-ot-shape-complex-indic.cc
index 4a5f363..089df06 100644
--- a/src/hb-ot-shape-complex-indic.cc
+++ b/src/hb-ot-shape-complex-indic.cc
@@ -650,11 +650,15 @@ final_reordering_syllable (hb_buffer_t *buffer,
       while (new_reph_pos > start && info[new_reph_pos].indic_position() == POS_SMVD)
 	new_reph_pos--;
 
-      if (unlikely (info[new_reph_pos].indic_category() == OT_H)) {
-	/* *If* the Reph is to be ending up after a Matra,Halant sequence,
-	 * position it before that Halant so it can interact with the Matra.
-	 * However, if it's a plain Consonant,Halant we shouldn't do that.
-	 */
+      /*
+       * If the Reph is to be ending up after a Matra,Halant sequence,
+       * position it before that Halant so it can interact with the Matra.
+       * However, if it's a plain Consonant,Halant we shouldn't do that.
+       * Uniscribe doesn't do this.
+       * TEST: U+0930,U+094D,U+0915,U+094B,U+094D
+       */
+      if (!options.uniscribe_bug_compatible &&
+	  unlikely (info[new_reph_pos].indic_category() == OT_H)) {
 	for (unsigned int i = base + 1; i < new_reph_pos; i++)
 	  if (info[i].indic_category() == OT_M) {
 	    /* Ok, got it. */
commit 67ea29af49bb08ee679914076808327992cf6676
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Fri May 11 16:51:23 2012 +0200

    [Indic] Add example of different Uniscribe behavior

diff --git a/src/hb-ot-shape-complex-indic.cc b/src/hb-ot-shape-complex-indic.cc
index 4d0c2be..4a5f363 100644
--- a/src/hb-ot-shape-complex-indic.cc
+++ b/src/hb-ot-shape-complex-indic.cc
@@ -373,6 +373,10 @@ initial_reordering_consonant_syllable (const hb_ot_map_t *map, hb_buffer_t *buff
       if ((FLAG (info[i].indic_category()) & (FLAG (OT_ZWNJ) | FLAG (OT_ZWJ) | FLAG (OT_N) | FLAG (OT_H))))
 	info[i].indic_position() = info[i - 1].indic_position();
   } else {
+    /*
+     * Uniscribe doesn't move the Halant with Left Matra.
+     * TEST: U+092B,U+093F,U+094DE
+     */
     /* Please update the non-Uniscribe branch when touching this! */
     for (unsigned int i = start + 1; i < end; i++)
       if ((FLAG (info[i].indic_category()) & (FLAG (OT_ZWNJ) | FLAG (OT_ZWJ) | FLAG (OT_N) | FLAG (OT_H)))) {
@@ -380,7 +384,6 @@ initial_reordering_consonant_syllable (const hb_ot_map_t *map, hb_buffer_t *buff
 	if (info[i].indic_category() == OT_H && info[i].indic_position() == POS_LEFT_MATRA)
 	  for (unsigned int j = i; j > start; j--)
 	    if (info[j - 1].indic_position() != POS_LEFT_MATRA) {
-	      /* Uniscribe doesn't move the Halant with Left Matra. */
 	      info[i].indic_position() = info[j - 1].indic_position();
 	      break;
 	    }
commit ebe29733d44fe0fa9fb30f946ab0dd7a40336a24
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Fri May 11 16:43:12 2012 +0200

    [Indic] Add runtime Uniscribe bug compatibility mode!
    
    Enable by setting envvar:
    
      HB_OT_INDIC_OPTIONS=uniscribe-bug-compatible
    
    Plus, LeftMatra+Halant "feature".

diff --git a/src/hb-ot-shape-complex-indic.cc b/src/hb-ot-shape-complex-indic.cc
index 0c6f021..4d0c2be 100644
--- a/src/hb-ot-shape-complex-indic.cc
+++ b/src/hb-ot-shape-complex-indic.cc
@@ -24,10 +24,19 @@
  * Google Author(s): Behdad Esfahbod
  */
 
-#define UNISCRIBE_BUG_COMPATIBLE 1
-
 #include "hb-ot-shape-complex-indic-private.hh"
 
+static const struct indic_options_t
+{
+  indic_options_t (void)
+  {
+    char *c = getenv ("HB_OT_INDIC_OPTIONS");
+    uniscribe_bug_compatible = c && strstr (c, "uniscribe-bug-compatible");
+  }
+
+  bool uniscribe_bug_compatible;
+} options;
+
 static int
 compare_codepoint (const void *pa, const void *pb)
 {
@@ -357,10 +366,26 @@ initial_reordering_consonant_syllable (const hb_ot_map_t *map, hb_buffer_t *buff
   }
 
   /* Attach ZWJ, ZWNJ, nukta, and halant to previous char to move with them. */
-  for (unsigned int i = start + 1; i < end; i++)
-    if ((FLAG (info[i].indic_category()) &
-	 (FLAG (OT_ZWNJ) | FLAG (OT_ZWJ) | FLAG (OT_N) | FLAG (OT_H))))
-      info[i].indic_position() = info[i - 1].indic_position();
+  if (!options.uniscribe_bug_compatible)
+  {
+    /* Please update the Uniscribe branch when touching this! */
+    for (unsigned int i = start + 1; i < end; i++)
+      if ((FLAG (info[i].indic_category()) & (FLAG (OT_ZWNJ) | FLAG (OT_ZWJ) | FLAG (OT_N) | FLAG (OT_H))))
+	info[i].indic_position() = info[i - 1].indic_position();
+  } else {
+    /* Please update the non-Uniscribe branch when touching this! */
+    for (unsigned int i = start + 1; i < end; i++)
+      if ((FLAG (info[i].indic_category()) & (FLAG (OT_ZWNJ) | FLAG (OT_ZWJ) | FLAG (OT_N) | FLAG (OT_H)))) {
+	info[i].indic_position() = info[i - 1].indic_position();
+	if (info[i].indic_category() == OT_H && info[i].indic_position() == POS_LEFT_MATRA)
+	  for (unsigned int j = i; j > start; j--)
+	    if (info[j - 1].indic_position() != POS_LEFT_MATRA) {
+	      /* Uniscribe doesn't move the Halant with Left Matra. */
+	      info[i].indic_position() = info[j - 1].indic_position();
+	      break;
+	    }
+      }
+  }
 
   /* We do bubble-sort, skip malicious clusters attempts */
   if (end - start < 64)
@@ -666,11 +691,8 @@ final_reordering_syllable (hb_buffer_t *buffer,
 
   /* Finish off the clusters and go home! */
 
-  /* For testing purposes we want to enable this to test against Uniscribe.
-   * One day when we don't compare to Uniscribe output anymore, we want to
-   * disable this because we believe it would make for superior cursor
-   * positioning. */
-  if (UNISCRIBE_BUG_COMPATIBLE) {
+  if (!options.uniscribe_bug_compatible)
+  {
     /* This is what Uniscribe does.  Ie. add cluster boundaries after Halant,ZWNJ.
      * This means, half forms are submerged into the main consonants cluster.
      * This is unnecessary, and makes cursor positioning harder, but that's what
commit 616e692e2950d326b6c46aba5b5bead3cc29d315
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Fri May 11 16:25:02 2012 +0200

    [Indic] Add #define UNISCRIBE_BUG_COMPATIBLE 1

diff --git a/src/hb-ot-shape-complex-indic.cc b/src/hb-ot-shape-complex-indic.cc
index f22f762..0c6f021 100644
--- a/src/hb-ot-shape-complex-indic.cc
+++ b/src/hb-ot-shape-complex-indic.cc
@@ -24,6 +24,7 @@
  * Google Author(s): Behdad Esfahbod
  */
 
+#define UNISCRIBE_BUG_COMPATIBLE 1
 
 #include "hb-ot-shape-complex-indic-private.hh"
 
@@ -669,7 +670,7 @@ final_reordering_syllable (hb_buffer_t *buffer,
    * One day when we don't compare to Uniscribe output anymore, we want to
    * disable this because we believe it would make for superior cursor
    * positioning. */
-  if (1) {
+  if (UNISCRIBE_BUG_COMPATIBLE) {
     /* This is what Uniscribe does.  Ie. add cluster boundaries after Halant,ZWNJ.
      * This means, half forms are submerged into the main consonants cluster.
      * This is unnecessary, and makes cursor positioning harder, but that's what
commit 6782bdae3be0357da1dadc7b806a43ceefa67a90
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Fri May 11 16:23:43 2012 +0200

    [Indic] Fix Left Matra + Halant reordering
    
    As can be seen in: U+092B,U+093F,U+094D

diff --git a/src/hb-ot-shape-complex-indic.cc b/src/hb-ot-shape-complex-indic.cc
index 99c2268..f22f762 100644
--- a/src/hb-ot-shape-complex-indic.cc
+++ b/src/hb-ot-shape-complex-indic.cc
@@ -500,8 +500,10 @@ final_reordering_syllable (hb_buffer_t *buffer,
   while (new_matra_pos > start &&
 	 !(FLAG (info[new_matra_pos].indic_category()) & (FLAG (OT_M) | FLAG (OT_H))))
     new_matra_pos--;
-  /* If we found no Halant we are done.  Otherwise... */
-  if (info[new_matra_pos].indic_category() == OT_H) {
+  /* If we found no Halant we are done.  Otherwise only proceed if the Halant does
+   * not belong to the Matra itself! */
+  if (info[new_matra_pos].indic_category() == OT_H &&
+      info[new_matra_pos].indic_position() != POS_LEFT_MATRA) {
     /* -> If ZWJ or ZWNJ follow this halant, position is moved after it. */
     if (new_matra_pos + 1 < end && is_joiner (info[new_matra_pos + 1]))
       new_matra_pos++;
commit 3c2ea9481b1028e927e615a5434ebf8edcb5f891
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Fri May 11 16:23:38 2012 +0200

    Minor

diff --git a/src/hb-ot-shape-complex-indic.cc b/src/hb-ot-shape-complex-indic.cc
index 3b0cc6f..99c2268 100644
--- a/src/hb-ot-shape-complex-indic.cc
+++ b/src/hb-ot-shape-complex-indic.cc
@@ -213,7 +213,6 @@ static void
 initial_reordering_consonant_syllable (const hb_ot_map_t *map, hb_buffer_t *buffer, hb_mask_t *mask_array,
 				       unsigned int start, unsigned int end)
 {
-  unsigned int i;
   hb_glyph_info_t *info = buffer->info;
 
   /* Comments from:
@@ -251,37 +250,39 @@ initial_reordering_consonant_syllable (const hb_ot_map_t *map, hb_buffer_t *buff
     has_reph = true;
   };
 
-  /* -> starting from the end of the syllable, move backwards */
-  i = end;
-  do {
-    i--;
-    /* -> until a consonant is found */
-    if (is_consonant (info[i]))
-    {
-      /* -> that does not have a below-base or post-base form
-       * (post-base forms have to follow below-base forms), */
-      if (info[i].indic_position() != POS_BELOW_C &&
-	  info[i].indic_position() != POS_POST_C)
+  {
+    /* -> starting from the end of the syllable, move backwards */
+    unsigned int i = end;
+    do {
+      i--;
+      /* -> until a consonant is found */
+      if (is_consonant (info[i]))
       {
-        base = i;
-	break;
-      }
+	/* -> that does not have a below-base or post-base form
+	 * (post-base forms have to follow below-base forms), */
+	if (info[i].indic_position() != POS_BELOW_C &&
+	    info[i].indic_position() != POS_POST_C)
+	{
+	  base = i;
+	  break;
+	}
 
-      /* -> or that is not a pre-base reordering Ra,
-       *
-       * TODO
-       */
+	/* -> or that is not a pre-base reordering Ra,
+	 *
+	 * TODO
+	 */
 
-      /* -> or arrive at the first consonant. The consonant stopped at will
-       * be the base. */
-      base = i;
-    }
-    else
-      if (is_joiner (info[i]))
-        break;
-  } while (i > limit);
-  if (base < start)
-    base = start; /* Just in case... */
+	/* -> or arrive at the first consonant. The consonant stopped at will
+	 * be the base. */
+	base = i;
+      }
+      else
+	if (is_joiner (info[i]))
+	  break;
+    } while (i > limit);
+    if (base < start)
+      base = start; /* Just in case... */
+  }
 
   /* -> If the syllable starts with Ra + Halant (in a script that has Reph)
    *    and has more than one consonant, Ra is excluded from candidates for
@@ -326,7 +327,7 @@ initial_reordering_consonant_syllable (const hb_ot_map_t *map, hb_buffer_t *buff
 
   /* Reorder characters */
 
-  for (i = start; i < base; i++)
+  for (unsigned int i = start; i < base; i++)
     info[i].indic_position() = POS_PRE_C;
   info[base].indic_position() = POS_BASE_C;
 
@@ -338,7 +339,7 @@ initial_reordering_consonant_syllable (const hb_ot_map_t *map, hb_buffer_t *buff
    * last consonant. */
   if ((map->get_chosen_script (0) & 0x000000FF) != '2') {
     /* We should only do this for Indic scripts which have a version two I guess. */
-    for (i = base + 1; i < end; i++)
+    for (unsigned int i = base + 1; i < end; i++)
       if (info[i].indic_category() == OT_H) {
         unsigned int j;
         for (j = end - 1; j > i; j--)
@@ -355,7 +356,7 @@ initial_reordering_consonant_syllable (const hb_ot_map_t *map, hb_buffer_t *buff
   }
 
   /* Attach ZWJ, ZWNJ, nukta, and halant to previous char to move with them. */
-  for (i = start + 1; i < end; i++)
+  for (unsigned int i = start + 1; i < end; i++)
     if ((FLAG (info[i].indic_category()) &
 	 (FLAG (OT_ZWNJ) | FLAG (OT_ZWJ) | FLAG (OT_N) | FLAG (OT_H))))
       info[i].indic_position() = info[i - 1].indic_position();
@@ -367,7 +368,7 @@ initial_reordering_consonant_syllable (const hb_ot_map_t *map, hb_buffer_t *buff
     hb_bubble_sort (info + start, end - start, compare_indic_order);
     /* Find base again */
     base = end;
-    for (i = start; i < end; i++)
+    for (unsigned int i = start; i < end; i++)
       if (info[i].indic_position() == POS_BASE_C) {
         base = i;
 	break;
@@ -385,19 +386,19 @@ initial_reordering_consonant_syllable (const hb_ot_map_t *map, hb_buffer_t *buff
 
     /* Pre-base */
     mask = mask_array[HALF] | mask_array[AKHN] | mask_array[CJCT];
-    for (i = start; i < base; i++)
+    for (unsigned int i = start; i < base; i++)
       info[i].mask  |= mask;
     /* Base */
     mask = mask_array[AKHN] | mask_array[CJCT];
     info[base].mask |= mask;
     /* Post-base */
     mask = mask_array[BLWF] | mask_array[PSTF] | mask_array[CJCT];
-    for (i = base + 1; i < end; i++)
+    for (unsigned int i = base + 1; i < end; i++)
       info[i].mask  |= mask;
   }
 
   /* Apply ZWJ/ZWNJ effects */
-  for (i = start + 1; i < end; i++)
+  for (unsigned int i = start + 1; i < end; i++)
     if (is_joiner (info[i])) {
       bool non_joiner = info[i].indic_category() == OT_ZWNJ;
       unsigned int j = i;
commit c071b99f150a9344a2056dfeba8c613f8a5602db
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Fri May 11 16:22:46 2012 +0200

    [Indic] Add test for Left Matra with Halant
    
    Uniscribe doesn't move the Halant, we do.  And do a broken job of it now.

diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-devanagari/misc/tricky-reordering.txt b/test/shaping/texts/in-tree/shaper-indic/indic/script-devanagari/misc/tricky-reordering.txt
index 6c6bcea..1723ced 100644
--- a/test/shaping/texts/in-tree/shaper-indic/indic/script-devanagari/misc/tricky-reordering.txt
+++ b/test/shaping/texts/in-tree/shaper-indic/indic/script-devanagari/misc/tricky-reordering.txt
@@ -2,3 +2,4 @@ Usage:
   ./hb-unicode-encode UNICODE_STRING...
 or:
   ./hb-unicode-encode --stdin
+फि्
commit 203d71069c45048b6dd8fa22b61fd8f2c844b4f6
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Fri May 11 16:01:44 2012 +0200

    [GSUB/GPOS] Check all glyph masks when matching input

diff --git a/src/hb-ot-layout-gsubgpos-private.hh b/src/hb-ot-layout-gsubgpos-private.hh
index 1785f55..03a561a 100644
--- a/src/hb-ot-layout-gsubgpos-private.hh
+++ b/src/hb-ot-layout-gsubgpos-private.hh
@@ -134,11 +134,13 @@ struct hb_apply_context_t
   {
     inline mark_skipping_forward_iterator_t (hb_apply_context_t *c_,
 					     unsigned int start_index_,
-					     unsigned int num_items_)
+					     unsigned int num_items_,
+					     hb_mask_t mask_ = 0)
     {
       c = c_;
       idx = start_index_;
       num_items = num_items_;
+      mask = mask_ ? mask_ : c->lookup_mask;
       end = MIN (c->buffer->len, c->buffer->idx + c->context_length);
     }
     inline bool has_no_chance (void) const
@@ -156,7 +158,7 @@ struct hb_apply_context_t
 	idx++;
       } while (_hb_ot_layout_skip_mark (c->face, &c->buffer->info[idx], lookup_props, property_out));
       num_items--;
-      return true;
+      return !!(c->buffer->info[idx].mask & mask);
     }
     inline bool next (unsigned int *property_out = NULL)
     {
@@ -167,6 +169,7 @@ struct hb_apply_context_t
     private:
     hb_apply_context_t *c;
     unsigned int num_items;
+    hb_mask_t mask;
     unsigned int end;
   };
 
@@ -174,11 +177,13 @@ struct hb_apply_context_t
   {
     inline mark_skipping_backward_iterator_t (hb_apply_context_t *c_,
 					      unsigned int start_index_,
-					      unsigned int num_items_)
+					      unsigned int num_items_,
+					      hb_mask_t mask_ = 0)
     {
       c = c_;
       idx = start_index_;
       num_items = num_items_;
+      mask = mask_ ? mask_ : c->lookup_mask;
     }
     inline bool has_no_chance (void) const
     {
@@ -195,7 +200,7 @@ struct hb_apply_context_t
 	idx--;
       } while (_hb_ot_layout_skip_mark (c->face, &c->buffer->out_info[idx], lookup_props, property_out));
       num_items--;
-      return true;
+      return !!(c->buffer->info[idx].mask & mask);
     }
     inline bool prev (unsigned int *property_out = NULL)
     {
@@ -206,6 +211,7 @@ struct hb_apply_context_t
     private:
     hb_apply_context_t *c;
     unsigned int num_items;
+    hb_mask_t mask;
   };
 
   inline bool should_mark_skip_current_glyph (void) const
@@ -335,7 +341,7 @@ static inline bool match_backtrack (hb_apply_context_t *c,
 				    match_func_t match_func,
 				    const void *match_data)
 {
-  hb_apply_context_t::mark_skipping_backward_iterator_t skippy_iter (c, c->buffer->backtrack_len (), count);
+  hb_apply_context_t::mark_skipping_backward_iterator_t skippy_iter (c, c->buffer->backtrack_len (), count, (hb_mask_t) -1);
   if (skippy_iter.has_no_chance ())
     return false;
 
@@ -358,7 +364,7 @@ static inline bool match_lookahead (hb_apply_context_t *c,
 				    const void *match_data,
 				    unsigned int offset)
 {
-  hb_apply_context_t::mark_skipping_forward_iterator_t skippy_iter (c, c->buffer->idx + offset - 1, count);
+  hb_apply_context_t::mark_skipping_forward_iterator_t skippy_iter (c, c->buffer->idx + offset - 1, count, (hb_mask_t) -1);
   if (skippy_iter.has_no_chance ())
     return false;
 
commit 668c6046c1b3af3bd316bda0cc8636f2a5e8df42
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Fri May 11 15:34:13 2012 +0200

    [Indic] Apply Reph mask to all POS_REPH glyphs
    
    Needed for upcoming changes to GSUB/GPOS mask matching.

diff --git a/src/hb-ot-shape-complex-indic.cc b/src/hb-ot-shape-complex-indic.cc
index dbd54f0..3b0cc6f 100644
--- a/src/hb-ot-shape-complex-indic.cc
+++ b/src/hb-ot-shape-complex-indic.cc
@@ -380,8 +380,8 @@ initial_reordering_consonant_syllable (const hb_ot_map_t *map, hb_buffer_t *buff
     hb_mask_t mask;
 
     /* Reph */
-    if (has_reph)
-      info[start].mask |= mask_array[RPHF];
+    for (unsigned int i = start; i < end && info[i].indic_position() == POS_RA_TO_BECOME_REPH; i++)
+      info[i].mask |= mask_array[RPHF];
 
     /* Pre-base */
     mask = mask_array[HALF] | mask_array[AKHN] | mask_array[CJCT];
commit 4be46bade26faf13f7b4d447e9cc608e183955dc
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Fri May 11 14:39:01 2012 +0200

    [Indic] Fix state machine to backtrack

diff --git a/src/hb-ot-shape-complex-indic-machine.rl b/src/hb-ot-shape-complex-indic-machine.rl
index c53bd78..ddecfcc 100644
--- a/src/hb-ot-shape-complex-indic-machine.rl
+++ b/src/hb-ot-shape-complex-indic-machine.rl
@@ -59,39 +59,35 @@ z = ZWJ|ZWNJ;
 matra_group = M N? H?;
 syllable_tail = SM? (VD VD?)?;
 
-action found_consonant_syllable { initial_reordering_consonant_syllable (map, buffer, mask_array, last, p); }
-action found_vowel_syllable { initial_reordering_vowel_syllable (map, buffer, mask_array, last, p); }
-action found_standalone_cluster { initial_reordering_standalone_cluster (map, buffer, mask_array, last, p); }
-action found_non_indic { initial_reordering_non_indic (map, buffer, mask_array, last, p); }
-
-action next_syllable {
-  for (unsigned int i = last; i < p; i++)
-    info[i].syllable() = syllable_serial;
-  last = p;
-  syllable_serial++;
-}
 
-consonant_syllable =	(c.N? (H.z?|z.H))* c.N? A? (H.z? | matra_group*)? syllable_tail %(found_consonant_syllable);
-vowel_syllable =	(Ra H)? V N? (z?.H.c | ZWJ.c)? matra_group* syllable_tail %(found_vowel_syllable);
-standalone_cluster =	(Ra H)? NBSP N? (z? H c)? matra_group* syllable_tail %(found_standalone_cluster);
-other = /./ %(found_non_indic);
+consonant_syllable =	(c.N? (H.z?|z.H))* c.N? A? (H.z? | matra_group*)? syllable_tail;
+vowel_syllable =	(Ra H)? V N? (z?.H.c | ZWJ.c)? matra_group* syllable_tail;
+standalone_cluster =	(Ra H)? NBSP N? (z? H c)? matra_group* syllable_tail;
+other =			any;
 
-syllable =
-	  consonant_syllable
-	| vowel_syllable
-	| standalone_cluster
-	| other
-	;
+main := |*
+	consonant_syllable	=> { process_syllable (consonant_syllable); };
+	vowel_syllable		=> { process_syllable (vowel_syllable); };
+	standalone_cluster	=> { process_syllable (standalone_cluster); };
+	other			=> { process_syllable (non_indic); };
+*|;
 
-main := (syllable %(next_syllable))**;
 
 }%%
 
+#define process_syllable(func) \
+  HB_STMT_START { \
+    for (unsigned int i = last; i < p+1; i++) \
+      info[i].syllable() = syllable_serial; \
+    PASTE (initial_reordering_, func) (map, buffer, mask_array, last, p+1); \
+    last = p+1; \
+    syllable_serial++; \
+  } HB_STMT_END
 
 static void
 find_syllables (const hb_ot_map_t *map, hb_buffer_t *buffer, hb_mask_t *mask_array)
 {
-  unsigned int p, pe, eof;
+  unsigned int p, pe, eof, ts, te, act;
   int cs;
   hb_glyph_info_t *info = buffer->info;
   %%{
diff --git a/src/hb-private.hh b/src/hb-private.hh
index 4bedcb2..0423dd4 100644
--- a/src/hb-private.hh
+++ b/src/hb-private.hh
@@ -91,6 +91,8 @@ template <typename Type> static inline Type MAX (const Type &a, const Type &b) {
 #define ASSERT_STATIC_EXPR(_cond) ((void) sizeof (char[(_cond) ? 1 : -1]))
 #define ASSERT_STATIC_EXPR_ZERO(_cond) (0 * sizeof (char[(_cond) ? 1 : -1]))
 
+#define _PASTE1(a,b) a##b
+#define PASTE(a,b) _PASTE1(a,b)
 
 /* Lets assert int types.  Saves trouble down the road. */
 
commit cee7187447b76b22e1bb6136d137b35ac49c3a5d
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Fri May 11 11:41:39 2012 +0200

    [Indic] Move syllable tracking from Indic to generic layer
    
    This is to incorporate it into GSUB/GPOS processing.

diff --git a/src/hb-ot-layout-gpos-table.hh b/src/hb-ot-layout-gpos-table.hh
index 4bd8708..44ec65f 100644
--- a/src/hb-ot-layout-gpos-table.hh
+++ b/src/hb-ot-layout-gpos-table.hh
@@ -1501,6 +1501,7 @@ GPOS::position_finish (hb_buffer_t *buffer)
   for (unsigned int i = 0; i < len; i++)
     fix_mark_attachment (pos, i, direction);
 
+  HB_BUFFER_DEALLOCATE_VAR (buffer, syllable);
   HB_BUFFER_DEALLOCATE_VAR (buffer, lig_props);
   HB_BUFFER_DEALLOCATE_VAR (buffer, props_cache);
 }
diff --git a/src/hb-ot-layout-gsub-table.hh b/src/hb-ot-layout-gsub-table.hh
index 682303a..bc2b0e1 100644
--- a/src/hb-ot-layout-gsub-table.hh
+++ b/src/hb-ot-layout-gsub-table.hh
@@ -1169,10 +1169,11 @@ GSUB::substitute_start (hb_buffer_t *buffer)
 {
   HB_BUFFER_ALLOCATE_VAR (buffer, props_cache);
   HB_BUFFER_ALLOCATE_VAR (buffer, lig_props);
+  HB_BUFFER_ALLOCATE_VAR (buffer, syllable);
 
   unsigned int count = buffer->len;
   for (unsigned int i = 0; i < count; i++)
-    buffer->info[i].props_cache() = buffer->info[i].lig_props() = 0;
+    buffer->info[i].props_cache() = buffer->info[i].lig_props() = buffer->info[i].syllable() = 0;
 }
 
 void
diff --git a/src/hb-ot-layout-gsubgpos-private.hh b/src/hb-ot-layout-gsubgpos-private.hh
index 904c344..1785f55 100644
--- a/src/hb-ot-layout-gsubgpos-private.hh
+++ b/src/hb-ot-layout-gsubgpos-private.hh
@@ -34,9 +34,6 @@
 
 
 
-/* buffer var allocations */
-#define lig_props() var2.u8[3]
-
 /* unique ligature id */
 /* component number in the ligature (0 = base) */
 static inline void
diff --git a/src/hb-ot-layout-private.hh b/src/hb-ot-layout-private.hh
index bf7e43b..f860e7b 100644
--- a/src/hb-ot-layout-private.hh
+++ b/src/hb-ot-layout-private.hh
@@ -33,6 +33,7 @@
 
 #include "hb-font-private.hh"
 #include "hb-buffer-private.hh"
+#include "hb-ot-shape-complex-private.hh"
 
 
 
@@ -40,9 +41,6 @@
  * GDEF
  */
 
-/* buffer var allocations */
-#define props_cache() var1.u16[1] /* glyph_props cache */
-
 /* XXX cleanup */
 typedef enum {
   HB_OT_LAYOUT_GLYPH_CLASS_UNCLASSIFIED	= 0x0001,
diff --git a/src/hb-ot-shape-complex-indic-machine.rl b/src/hb-ot-shape-complex-indic-machine.rl
index 2ec2798..c53bd78 100644
--- a/src/hb-ot-shape-complex-indic-machine.rl
+++ b/src/hb-ot-shape-complex-indic-machine.rl
@@ -66,7 +66,7 @@ action found_non_indic { initial_reordering_non_indic (map, buffer, mask_array,
 
 action next_syllable {
   for (unsigned int i = last; i < p; i++)
-    info[i].indic_syllable() = syllable_serial;
+    info[i].syllable() = syllable_serial;
   last = p;
   syllable_serial++;
 }
diff --git a/src/hb-ot-shape-complex-indic-private.hh b/src/hb-ot-shape-complex-indic-private.hh
index d63b61a..0d18536 100644
--- a/src/hb-ot-shape-complex-indic-private.hh
+++ b/src/hb-ot-shape-complex-indic-private.hh
@@ -36,7 +36,6 @@
 /* buffer var allocations */
 #define indic_category() complex_var_persistent_u8_0() /* indic_category_t */
 #define indic_position() complex_var_persistent_u8_1() /* indic_matra_category_t */
-#define indic_syllable() complex_var_persistent_u8_2() /* serial */
 
 #define INDIC_TABLE_ELEMENT_TYPE uint8_t
 
diff --git a/src/hb-ot-shape-complex-indic.cc b/src/hb-ot-shape-complex-indic.cc
index e96dd78..dbd54f0 100644
--- a/src/hb-ot-shape-complex-indic.cc
+++ b/src/hb-ot-shape-complex-indic.cc
@@ -168,7 +168,6 @@ _hb_ot_shape_complex_setup_masks_indic (hb_ot_map_t *map, hb_buffer_t *buffer, h
 {
   HB_BUFFER_ALLOCATE_VAR (buffer, indic_category);
   HB_BUFFER_ALLOCATE_VAR (buffer, indic_position);
-  HB_BUFFER_ALLOCATE_VAR (buffer, indic_syllable);
 
   /* We cannot setup masks here.  We save information about characters
    * and setup masks later on in a pause-callback. */
@@ -697,16 +696,15 @@ final_reordering (const hb_ot_map_t *map,
 
   hb_glyph_info_t *info = buffer->info;
   unsigned int last = 0;
-  unsigned int last_syllable = info[0].indic_syllable();
+  unsigned int last_syllable = info[0].syllable();
   for (unsigned int i = 1; i < count; i++)
-    if (last_syllable != info[i].indic_syllable()) {
+    if (last_syllable != info[i].syllable()) {
       final_reordering_syllable (buffer, last, i);
       last = i;
-      last_syllable = info[last].indic_syllable();
+      last_syllable = info[last].syllable();
     }
   final_reordering_syllable (buffer, last, count);
 
-  HB_BUFFER_DEALLOCATE_VAR (buffer, indic_syllable);
   HB_BUFFER_DEALLOCATE_VAR (buffer, indic_category);
   HB_BUFFER_DEALLOCATE_VAR (buffer, indic_position);
 }
diff --git a/src/hb-ot-shape-complex-private.hh b/src/hb-ot-shape-complex-private.hh
index a4bc2f8..9b61f8e 100644
--- a/src/hb-ot-shape-complex-private.hh
+++ b/src/hb-ot-shape-complex-private.hh
@@ -38,12 +38,15 @@
 #define unicode_props0()	var1.u8[0]
 #define unicode_props1()	var1.u8[1]
 
+/* buffer var allocations, used during the GSUB/GPOS processing */
+#define props_cache()		var1.u16[1] /* GSUB/GPOS glyph_props cache */
+#define syllable()		var2.u8[0] /* GSUB/GPOS shaping boundaries */
+#define lig_props()		var2.u8[1] /* GSUB/GPOS ligature tracking */
+
 /* buffer var allocations, used by complex shapers */
-#define complex_var_persistent_u8_0()	var2.u8[0]
-#define complex_var_persistent_u8_1()	var2.u8[1]
-#define complex_var_persistent_u8_2()	var2.u8[2]
-#define complex_var_persistent_u16()	var2.u16[0]
-#define complex_var_temporary_u8()	var2.u8[3]
+#define complex_var_persistent_u8_0()	var2.u8[2]
+#define complex_var_persistent_u8_1()	var2.u8[3]
+#define complex_var_temporary_u8()	var2.u8[0]
 
 
 #define HB_COMPLEX_SHAPERS_IMPLEMENT_SHAPERS \
commit 3bf27a9f0e92aa31b464bd3b9fdea5933c9ae8b1
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Fri May 11 11:17:23 2012 +0200

    [Indic] Disable conjuncts when a ZWJ happens
    
    Not that the code makes any difference since the presence of ZWJ itself
    causes the ligature to fail to match anyway.

diff --git a/src/hb-ot-shape-complex-indic.cc b/src/hb-ot-shape-complex-indic.cc
index e3f78ce..e96dd78 100644
--- a/src/hb-ot-shape-complex-indic.cc
+++ b/src/hb-ot-shape-complex-indic.cc
@@ -406,12 +406,7 @@ initial_reordering_consonant_syllable (const hb_ot_map_t *map, hb_buffer_t *buff
       do {
 	j--;
 
-	/* Reading the Unicode and OpenType specs, I think the following line
-	 * is correct, but this is not what the test suite expects currently.
-	 * The test suite has been drinking, not me...  But disable while
-	 * investigating.
-	 */
-	//info[j].mask &= ~mask_array[CJCT];
+	info[j].mask &= ~mask_array[CJCT];
 	if (non_joiner)
 	  info[j].mask &= ~mask_array[HALF];
 
commit c6d904d67db589dd6209928e56504f04f6a07756
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Fri May 11 11:07:40 2012 +0200

    [Indic] Fix bitops typo!
    
    Another 1000 down!

diff --git a/src/hb-ot-shape-complex-indic.cc b/src/hb-ot-shape-complex-indic.cc
index 4d82e24..e3f78ce 100644
--- a/src/hb-ot-shape-complex-indic.cc
+++ b/src/hb-ot-shape-complex-indic.cc
@@ -411,9 +411,9 @@ initial_reordering_consonant_syllable (const hb_ot_map_t *map, hb_buffer_t *buff
 	 * The test suite has been drinking, not me...  But disable while
 	 * investigating.
 	 */
-	//info[j].mask &= !mask_array[CJCT];
+	//info[j].mask &= ~mask_array[CJCT];
 	if (non_joiner)
-	  info[j].mask &= !mask_array[HALF];
+	  info[j].mask &= ~mask_array[HALF];
 
       } while (j > start && !is_consonant (info[j]));
     }
commit 55fe2cf79b11d9a63ea33b3ee76bd0ebca345157
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Fri May 11 03:45:28 2012 +0200

    Make APPLY debug output print current index and codepoint
    
    Yay!

diff --git a/src/hb-ot-layout-gsubgpos-private.hh b/src/hb-ot-layout-gsubgpos-private.hh
index 9471ad3..904c344 100644
--- a/src/hb-ot-layout-gsubgpos-private.hh
+++ b/src/hb-ot-layout-gsubgpos-private.hh
@@ -98,7 +98,7 @@ struct hb_closure_context_t
 #endif
 
 #define TRACE_APPLY() \
-	hb_auto_trace_t<HB_DEBUG_APPLY> trace (&c->debug_depth, "APPLY", this, HB_FUNC, "");
+	hb_auto_trace_t<HB_DEBUG_APPLY> trace (&c->debug_depth, "APPLY", this, HB_FUNC, "idx %d codepoint %u", c->buffer->idx, c->buffer->info[c->buffer->idx].codepoint);
 
 
 
commit 7bd2b04fea5649d77d796d58b7f4918fe0378ee5
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Fri May 11 03:40:58 2012 +0200

    Minor

diff --git a/src/hb-object-private.hh b/src/hb-object-private.hh
index 23c1a44..754dcfa 100644
--- a/src/hb-object-private.hh
+++ b/src/hb-object-private.hh
@@ -197,10 +197,12 @@ struct _hb_object_header_t {
 
   inline void trace (const char *function) const {
     if (unlikely (!this)) return;
+    /* XXX We cannot use DEBUG_MSG_FUNC here since that one currecntly only
+     * prints the class name and throughs away the template info. */
     DEBUG_MSG (OBJECT, (void *) this,
-	       "refcount=%d %s",
-	       this ? ref_count.get_unsafe () : 0,
-	       function);
+	       "%s refcount=%d",
+	       function,
+	       this ? ref_count.get_unsafe () : 0);
   }
 
 };
commit cf26510dbbd8d38486e6ba423800db6427ade332
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Fri May 11 03:35:08 2012 +0200

    Some more...
    
    Done.  I promise.

diff --git a/src/hb-object-private.hh b/src/hb-object-private.hh
index 0cb5ebd..23c1a44 100644
--- a/src/hb-object-private.hh
+++ b/src/hb-object-private.hh
@@ -196,6 +196,7 @@ struct _hb_object_header_t {
   }
 
   inline void trace (const char *function) const {
+    if (unlikely (!this)) return;
     DEBUG_MSG (OBJECT, (void *) this,
 	       "refcount=%d %s",
 	       this ? ref_count.get_unsafe () : 0,
commit 9659523ca32b0e254d0e5fe387d817208d9cb6bf
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Fri May 11 03:33:36 2012 +0200

    More beauty in debug output!

diff --git a/src/hb-private.hh b/src/hb-private.hh
index aa2ea94..4bedcb2 100644
--- a/src/hb-private.hh
+++ b/src/hb-private.hh
@@ -610,11 +610,11 @@ _hb_debug_msg<0> (const char *what,
 template <int max_level>
 struct hb_auto_trace_t {
   explicit inline hb_auto_trace_t (unsigned int *plevel_,
-				   const char *what,
-				   const void *obj,
+				   const char *what_,
+				   const void *obj_,
 				   const char *func,
 				   const char *message,
-				   ...) : plevel(plevel_), returned (false)
+				   ...) : plevel (plevel_), what (what_), obj (obj_), returned (false)
   {
     if (plevel) ++*plevel;
 
@@ -627,7 +627,7 @@ struct hb_auto_trace_t {
   {
     if (unlikely (!returned)) {
       fprintf (stderr, "OUCH, returned with no call to TRACE_RETURN.  This is a bug, please report.  Level was %d.\n", plevel ? *plevel : -1);
-      _hb_debug_msg<max_level> (NULL, NULL, NULL, TRUE, plevel ? *plevel : 1, -1, " ");
+      _hb_debug_msg<max_level> (what, obj, NULL, TRUE, plevel ? *plevel : 1, -1, " ");
       return;
     }
 
@@ -641,7 +641,7 @@ struct hb_auto_trace_t {
       return v;
     }
 
-    _hb_debug_msg<max_level> (NULL, NULL, NULL, TRUE, plevel ? *plevel : 1, -1, "return %s", v ? "true" : "false");
+    _hb_debug_msg<max_level> (what, obj, NULL, TRUE, plevel ? *plevel : 1, -1, "return %s", v ? "true" : "false");
     if (plevel) --*plevel;
     plevel = NULL;
     returned = true;
@@ -651,6 +651,8 @@ struct hb_auto_trace_t {
   private:
   unsigned int *plevel;
   bool returned;
+  const char *what;
+  const void *obj;
 };
 template <> /* Optimize when tracing is disabled */
 struct hb_auto_trace_t<0> {
commit cf26e88a5ab477295479f5b9450c2019b6430eaa
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Fri May 11 03:16:57 2012 +0200

    Finish off debug output beautification

diff --git a/src/hb-open-type-private.hh b/src/hb-open-type-private.hh
index 5bd9827..f4a2ba9 100644
--- a/src/hb-open-type-private.hh
+++ b/src/hb-open-type-private.hh
@@ -166,7 +166,7 @@ struct hb_sanitize_context_t
     this->writable = false;
   }
 
-  inline void setup (void)
+  inline void start_processing (void)
   {
     this->start = hb_blob_get_data (this->blob, NULL);
     this->end = this->start + hb_blob_get_length (this->blob);
@@ -174,15 +174,15 @@ struct hb_sanitize_context_t
     this->debug_depth = 0;
 
     DEBUG_MSG_LEVEL (SANITIZE, this->blob, 0, +1,
-		     "init [%p..%p] (%lu bytes)",
+		     "start [%p..%p] (%lu bytes)",
 		     this->start, this->end,
 		     (unsigned long) (this->end - this->start));
   }
 
-  inline void finish (void)
+  inline void end_processing (void)
   {
     DEBUG_MSG_LEVEL (SANITIZE, this->blob, 0, -1,
-		     "fini [%p..%p] %u edit requests",
+		     "end [%p..%p] %u edit requests",
 		     this->start, this->end, this->edit_count);
 
     hb_blob_destroy (this->blob);
@@ -193,17 +193,13 @@ struct hb_sanitize_context_t
   inline bool check_range (const void *base, unsigned int len) const
   {
     const char *p = (const char *) base;
-    bool ret = this->start <= p &&
-	       p <= this->end &&
-	       (unsigned int) (this->end - p) >= len;
 
-    DEBUG_MSG_LEVEL (SANITIZE, this->blob, this->debug_depth + 1, 0,
-		     "range [%p..%p] (%d bytes) in [%p..%p] -> %s",
-		     p, p + len, len,
-		     this->start, this->end,
-		     ret ? "pass" : "FAIL");
+    hb_auto_trace_t<HB_DEBUG_SANITIZE> trace (&this->debug_depth, "SANITIZE", this->blob, NULL,
+					      "check_range [%p..%p] (%d bytes) in [%p..%p]",
+					      p, p + len, len,
+					      this->start, this->end);
 
-    return likely (ret);
+    return TRACE_RETURN (likely (this->start <= p && p <= this->end && (unsigned int) (this->end - p) >= len));
   }
 
   inline bool check_array (const void *base, unsigned int record_size, unsigned int len) const
@@ -211,13 +207,12 @@ struct hb_sanitize_context_t
     const char *p = (const char *) base;
     bool overflows = _hb_unsigned_int_mul_overflows (len, record_size);
 
-    DEBUG_MSG_LEVEL (SANITIZE, this->blob, this->debug_depth + 1, 0,
-		     "array [%p..%p] (%d*%d=%ld bytes) in [%p..%p] -> %s",
-		     p, p + (record_size * len), record_size, len, (unsigned long) record_size * len,
-		     this->start, this->end,
-		     !overflows ? "does not overflow" : "OVERFLOWS FAIL");
+    hb_auto_trace_t<HB_DEBUG_SANITIZE> trace (&this->debug_depth, "SANITIZE", this->blob, NULL,
+					      "check_array [%p..%p] (%d*%d=%ld bytes) in [%p..%p]",
+					      p, p + (record_size * len), record_size, len, (unsigned long) record_size * len,
+					      this->start, this->end);
 
-    return likely (!overflows && this->check_range (base, record_size * len));
+    return TRACE_RETURN (likely (!overflows && this->check_range (base, record_size * len)));
   }
 
   template <typename Type>
@@ -226,22 +221,21 @@ struct hb_sanitize_context_t
     return likely (this->check_range (obj, obj->min_size));
   }
 
-  inline bool can_edit (const void *base HB_UNUSED, unsigned int len HB_UNUSED)
+  inline bool may_edit (const void *base HB_UNUSED, unsigned int len HB_UNUSED)
   {
     const char *p = (const char *) base;
     this->edit_count++;
 
-    DEBUG_MSG_LEVEL (SANITIZE, this->blob, this->debug_depth + 1, 0,
-		     "edit(%u) [%p..%p] (%d bytes) in [%p..%p] -> %s",
-		     this->edit_count,
-		     p, p + len, len,
-		     this->start, this->end,
-		     this->writable ? "granted" : "REJECTED");
+    hb_auto_trace_t<HB_DEBUG_SANITIZE> trace (&this->debug_depth, "SANITIZE", this->blob, NULL,
+					      "may_edit(%u) [%p..%p] (%d bytes) in [%p..%p] -> %s",
+					      this->edit_count,
+					      p, p + len, len,
+					      this->start, this->end);
 
-    return this->writable;
+    return TRACE_RETURN (this->writable);
   }
 
-  unsigned int debug_depth;
+  mutable unsigned int debug_depth;
   const char *start, *end;
   bool writable;
   unsigned int edit_count;
@@ -265,10 +259,10 @@ struct Sanitizer
   retry:
     DEBUG_MSG_FUNC (SANITIZE, blob, "start");
 
-    c->setup ();
+    c->start_processing ();
 
     if (unlikely (!c->start)) {
-      c->finish ();
+      c->end_processing ();
       return blob;
     }
 
@@ -302,7 +296,7 @@ struct Sanitizer
       }
     }
 
-    c->finish ();
+    c->end_processing ();
 
     DEBUG_MSG_FUNC (SANITIZE, blob, sane ? "PASSED" : "FAILED");
     if (sane)
@@ -514,7 +508,7 @@ struct GenericOffsetTo : OffsetType
   private:
   /* Set the offset to Null */
   inline bool neuter (hb_sanitize_context_t *c) {
-    if (c->can_edit (this, this->static_size)) {
+    if (c->may_edit (this, this->static_size)) {
       this->set (0); /* 0 is Null offset */
       return true;
     }
commit d7bba01a353efc7432c474dd8755a02db4abd2ae
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Fri May 11 02:46:26 2012 +0200

    Only print class name in debug output if there's one available

diff --git a/src/hb-private.hh b/src/hb-private.hh
index 1f7ba11..aa2ea94 100644
--- a/src/hb-private.hh
+++ b/src/hb-private.hh
@@ -514,8 +514,6 @@ _hb_debug_msg_va (const char *what,
   if (!_hb_debug (level, max_level))
     return;
 
-  static const char bars[] = "││││││││││││││││││││││││││││││││││││││││";
-
   fprintf (stderr, "%-10s", what ? what : "");
 
   if (obj)
@@ -523,19 +521,23 @@ _hb_debug_msg_va (const char *what,
   else
     fprintf (stderr, " %*s  ", (unsigned int) (2 * sizeof (void *)), "");
 
-  if (indented)
+  if (indented) {
+    static const char bars[] = "││││││││││││││││││││││││││││││││││││││││";
     fprintf (stderr, "%2d %s├%s",
 	     level,
 	     bars + sizeof (bars) - 1 - MIN ((unsigned int) sizeof (bars), 3 * level),
 	     level_dir ? (level_dir > 0 ? "╮" : "╯") : "╴");
-  else
+  } else
     fprintf (stderr, "   ├╴");
 
   if (func) {
     /* If there's a class name, just write that. */
     const char *dotdot = strstr (func, "::");
+    const char *space = strchr (func, ' ');
+    if (space && dotdot && space < dotdot)
+      func = space + 1;
     unsigned int func_len = dotdot ? dotdot - func : strlen (func);
-    fprintf (stderr, "%*s: ", func_len, func);
+    fprintf (stderr, "%.*s: ", func_len, func);
   }
 
   if (message)
commit 85f73fa8da1fbb864aef0f3a592b1d65e24d593d
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Fri May 11 02:40:42 2012 +0200

    Only printout class name in tracing, if one is available
    
    Makes debug output much more pleasant.

diff --git a/src/hb-private.hh b/src/hb-private.hh
index dd2c465..1f7ba11 100644
--- a/src/hb-private.hh
+++ b/src/hb-private.hh
@@ -531,8 +531,12 @@ _hb_debug_msg_va (const char *what,
   else
     fprintf (stderr, "   ├╴");
 
-  if (func)
-    fprintf (stderr, "%s: ", func);
+  if (func) {
+    /* If there's a class name, just write that. */
+    const char *dotdot = strstr (func, "::");
+    unsigned int func_len = dotdot ? dotdot - func : strlen (func);
+    fprintf (stderr, "%*s: ", func_len, func);
+  }
 
   if (message)
     vfprintf (stderr, message, ap);
commit 98619ce4fa650c593b030d06d2f89fec83a10015
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Fri May 11 02:34:06 2012 +0200

    Minor

diff --git a/src/hb-ot-layout-gsubgpos-private.hh b/src/hb-ot-layout-gsubgpos-private.hh
index c9abd51..9471ad3 100644
--- a/src/hb-ot-layout-gsubgpos-private.hh
+++ b/src/hb-ot-layout-gsubgpos-private.hh
@@ -72,6 +72,8 @@ static inline uint8_t allocate_lig_id (hb_buffer_t *buffer) {
 	hb_auto_trace_t<HB_DEBUG_CLOSURE> trace (&c->debug_depth, "CLOSURE", this, HB_FUNC, "");
 
 
+/* TODO Add TRACE_RETURN annotation for would_apply */
+
 
 struct hb_closure_context_t
 {
commit acea183e986dd378c6f95120fe0feb0586a8ef36
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Fri May 11 02:33:11 2012 +0200

    Add return annotation for APPLY

diff --git a/src/hb-ot-layout-gpos-table.hh b/src/hb-ot-layout-gpos-table.hh
index 1b3677e..4bd8708 100644
--- a/src/hb-ot-layout-gpos-table.hh
+++ b/src/hb-ot-layout-gpos-table.hh
@@ -412,7 +412,7 @@ struct MarkArray : ArrayOf<MarkRecord>	/* Array of MarkRecords--in Coverage orde
     o.attach_lookback() = c->buffer->idx - glyph_pos;
 
     c->buffer->idx++;
-    return true;
+    return TRACE_RETURN (true);
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
@@ -433,14 +433,13 @@ struct SinglePosFormat1
   {
     TRACE_APPLY ();
     unsigned int index = (this+coverage) (c->buffer->info[c->buffer->idx].codepoint);
-    if (likely (index == NOT_COVERED))
-      return false;
+    if (likely (index == NOT_COVERED)) return TRACE_RETURN (false);
 
     valueFormat.apply_value (c->font, c->direction, this,
 			     values, c->buffer->pos[c->buffer->idx]);
 
     c->buffer->idx++;
-    return true;
+    return TRACE_RETURN (true);
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
@@ -471,18 +470,16 @@ struct SinglePosFormat2
   {
     TRACE_APPLY ();
     unsigned int index = (this+coverage) (c->buffer->info[c->buffer->idx].codepoint);
-    if (likely (index == NOT_COVERED))
-      return false;
+    if (likely (index == NOT_COVERED)) return TRACE_RETURN (false);
 
-    if (likely (index >= valueCount))
-      return false;
+    if (likely (index >= valueCount)) return TRACE_RETURN (false);
 
     valueFormat.apply_value (c->font, c->direction, this,
 			     &values[index * valueFormat.get_len ()],
 			     c->buffer->pos[c->buffer->idx]);
 
     c->buffer->idx++;
-    return true;
+    return TRACE_RETURN (true);
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
@@ -513,9 +510,9 @@ struct SinglePos
   {
     TRACE_APPLY ();
     switch (u.format) {
-    case 1: return u.format1.apply (c);
-    case 2: return u.format2.apply (c);
-    default:return false;
+    case 1: return TRACE_RETURN (u.format1.apply (c));
+    case 2: return TRACE_RETURN (u.format2.apply (c));
+    default:return TRACE_RETURN (false);
     }
   }
 
@@ -578,12 +575,12 @@ struct PairSet
 	if (len2)
 	  pos++;
 	c->buffer->idx = pos;
-	return true;
+	return TRACE_RETURN (true);
       }
       record = &StructAtOffset<PairValueRecord> (record, record_size);
     }
 
-    return false;
+    return TRACE_RETURN (false);
   }
 
   struct sanitize_closure_t {
@@ -621,17 +618,14 @@ struct PairPosFormat1
   {
     TRACE_APPLY ();
     hb_apply_context_t::mark_skipping_forward_iterator_t skippy_iter (c, c->buffer->idx, 1);
-    if (skippy_iter.has_no_chance ())
-      return false;
+    if (skippy_iter.has_no_chance ()) return TRACE_RETURN (false);
 
     unsigned int index = (this+coverage) (c->buffer->info[c->buffer->idx].codepoint);
-    if (likely (index == NOT_COVERED))
-      return false;
+    if (likely (index == NOT_COVERED)) return TRACE_RETURN (false);
 
-    if (!skippy_iter.next ())
-      return false;
+    if (!skippy_iter.next ()) return TRACE_RETURN (false);
 
-    return (this+pairSet[index]).apply (c, &valueFormat1, skippy_iter.idx);
+    return TRACE_RETURN ((this+pairSet[index]).apply (c, &valueFormat1, skippy_iter.idx));
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
@@ -676,15 +670,12 @@ struct PairPosFormat2
   {
     TRACE_APPLY ();
     hb_apply_context_t::mark_skipping_forward_iterator_t skippy_iter (c, c->buffer->idx, 1);
-    if (skippy_iter.has_no_chance ())
-      return false;
+    if (skippy_iter.has_no_chance ()) return TRACE_RETURN (false);
 
     unsigned int index = (this+coverage) (c->buffer->info[c->buffer->idx].codepoint);
-    if (likely (index == NOT_COVERED))
-      return false;
+    if (likely (index == NOT_COVERED)) return TRACE_RETURN (false);
 
-    if (!skippy_iter.next ())
-      return false;
+    if (!skippy_iter.next ()) return TRACE_RETURN (false);
 
     unsigned int len1 = valueFormat1.get_len ();
     unsigned int len2 = valueFormat2.get_len ();
@@ -692,8 +683,7 @@ struct PairPosFormat2
 
     unsigned int klass1 = (this+classDef1) (c->buffer->info[c->buffer->idx].codepoint);
     unsigned int klass2 = (this+classDef2) (c->buffer->info[skippy_iter.idx].codepoint);
-    if (unlikely (klass1 >= class1Count || klass2 >= class2Count))
-      return false;
+    if (unlikely (klass1 >= class1Count || klass2 >= class2Count)) return TRACE_RETURN (false);
 
     const Value *v = &values[record_len * (klass1 * class2Count + klass2)];
     valueFormat1.apply_value (c->font, c->direction, this,
@@ -705,7 +695,7 @@ struct PairPosFormat2
     if (len2)
       c->buffer->idx++;
 
-    return true;
+    return TRACE_RETURN (true);
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
@@ -764,9 +754,9 @@ struct PairPos
   {
     TRACE_APPLY ();
     switch (u.format) {
-    case 1: return u.format1.apply (c);
-    case 2: return u.format2.apply (c);
-    default:return false;
+    case 1: return TRACE_RETURN (u.format1.apply (c));
+    case 2: return TRACE_RETURN (u.format2.apply (c));
+    default:return TRACE_RETURN (false);
     }
   }
 
@@ -821,23 +811,18 @@ struct CursivePosFormat1
     TRACE_APPLY ();
 
     /* We don't handle mark glyphs here. */
-    if (c->property & HB_OT_LAYOUT_GLYPH_CLASS_MARK)
-      return false;
+    if (c->property & HB_OT_LAYOUT_GLYPH_CLASS_MARK) return TRACE_RETURN (false);
 
     hb_apply_context_t::mark_skipping_forward_iterator_t skippy_iter (c, c->buffer->idx, 1);
-    if (skippy_iter.has_no_chance ())
-      return false;
+    if (skippy_iter.has_no_chance ()) return TRACE_RETURN (false);
 
     const EntryExitRecord &this_record = entryExitRecord[(this+coverage) (c->buffer->info[c->buffer->idx].codepoint)];
-    if (!this_record.exitAnchor)
-      return false;
+    if (!this_record.exitAnchor) return TRACE_RETURN (false);
 
-    if (!skippy_iter.next ())
-      return false;
+    if (!skippy_iter.next ()) return TRACE_RETURN (false);
 
     const EntryExitRecord &next_record = entryExitRecord[(this+coverage) (c->buffer->info[skippy_iter.idx].codepoint)];
-    if (!next_record.entryAnchor)
-      return false;
+    if (!next_record.entryAnchor) return TRACE_RETURN (false);
 
     unsigned int i = c->buffer->idx;
     unsigned int j = skippy_iter.idx;
@@ -900,7 +885,7 @@ struct CursivePosFormat1
     }
 
     c->buffer->idx = j;
-    return true;
+    return TRACE_RETURN (true);
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
@@ -929,8 +914,8 @@ struct CursivePos
   {
     TRACE_APPLY ();
     switch (u.format) {
-    case 1: return u.format1.apply (c);
-    default:return false;
+    case 1: return TRACE_RETURN (u.format1.apply (c));
+    default:return TRACE_RETURN (false);
     }
   }
 
@@ -965,24 +950,20 @@ struct MarkBasePosFormat1
   {
     TRACE_APPLY ();
     unsigned int mark_index = (this+markCoverage) (c->buffer->info[c->buffer->idx].codepoint);
-    if (likely (mark_index == NOT_COVERED))
-      return false;
+    if (likely (mark_index == NOT_COVERED)) return TRACE_RETURN (false);
 
     /* now we search backwards for a non-mark glyph */
     unsigned int property;
     hb_apply_context_t::mark_skipping_backward_iterator_t skippy_iter (c, c->buffer->idx, 1);
-    if (!skippy_iter.prev (&property, LookupFlag::IgnoreMarks))
-      return false;
+    if (!skippy_iter.prev (&property, LookupFlag::IgnoreMarks)) return TRACE_RETURN (false);
 
     /* The following assertion is too strong, so we've disabled it. */
-    if (!(property & HB_OT_LAYOUT_GLYPH_CLASS_BASE_GLYPH))
-    {/*return false;*/}
+    if (!(property & HB_OT_LAYOUT_GLYPH_CLASS_BASE_GLYPH)) {/*return TRACE_RETURN (false);*/}
 
     unsigned int base_index = (this+baseCoverage) (c->buffer->info[skippy_iter.idx].codepoint);
-    if (base_index == NOT_COVERED)
-      return false;
+    if (base_index == NOT_COVERED) return TRACE_RETURN (false);
 
-    return (this+markArray).apply (c, mark_index, base_index, this+baseArray, classCount, skippy_iter.idx);
+    return TRACE_RETURN ((this+markArray).apply (c, mark_index, base_index, this+baseArray, classCount, skippy_iter.idx));
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
@@ -1019,8 +1000,8 @@ struct MarkBasePos
   {
     TRACE_APPLY ();
     switch (u.format) {
-    case 1: return u.format1.apply (c);
-    default:return false;
+    case 1: return TRACE_RETURN (u.format1.apply (c));
+    default:return TRACE_RETURN (false);
     }
   }
 
@@ -1060,31 +1041,27 @@ struct MarkLigPosFormat1
   {
     TRACE_APPLY ();
     unsigned int mark_index = (this+markCoverage) (c->buffer->info[c->buffer->idx].codepoint);
-    if (likely (mark_index == NOT_COVERED))
-      return false;
+    if (likely (mark_index == NOT_COVERED)) return TRACE_RETURN (false);
 
     /* now we search backwards for a non-mark glyph */
     unsigned int property;
     hb_apply_context_t::mark_skipping_backward_iterator_t skippy_iter (c, c->buffer->idx, 1);
-    if (!skippy_iter.prev (&property, LookupFlag::IgnoreMarks))
-      return false;
+    if (!skippy_iter.prev (&property, LookupFlag::IgnoreMarks)) return TRACE_RETURN (false);
 
     /* The following assertion is too strong, so we've disabled it. */
-    if (!(property & HB_OT_LAYOUT_GLYPH_CLASS_LIGATURE))
-    {/*return false;*/}
+    if (!(property & HB_OT_LAYOUT_GLYPH_CLASS_LIGATURE)) {/*return TRACE_RETURN (false);*/}
 
     unsigned int j = skippy_iter.idx;
     unsigned int lig_index = (this+ligatureCoverage) (c->buffer->info[j].codepoint);
-    if (lig_index == NOT_COVERED)
-      return false;
+    if (lig_index == NOT_COVERED) return TRACE_RETURN (false);
 
     const LigatureArray& lig_array = this+ligatureArray;
     const LigatureAttach& lig_attach = lig_array[lig_index];
 
     /* Find component to attach to */
     unsigned int comp_count = lig_attach.rows;
-    if (unlikely (!comp_count))
-      return false;
+    if (unlikely (!comp_count)) return TRACE_RETURN (false);
+
     unsigned int comp_index;
     /* We must now check whether the ligature ID of the current mark glyph
      * is identical to the ligature ID of the found ligature.  If yes, we
@@ -1101,7 +1078,7 @@ struct MarkLigPosFormat1
     else
       comp_index = comp_count - 1;
 
-    return (this+markArray).apply (c, mark_index, comp_index, lig_attach, classCount, j);
+    return TRACE_RETURN ((this+markArray).apply (c, mark_index, comp_index, lig_attach, classCount, j));
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
@@ -1139,8 +1116,8 @@ struct MarkLigPos
   {
     TRACE_APPLY ();
     switch (u.format) {
-    case 1: return u.format1.apply (c);
-    default:return false;
+    case 1: return TRACE_RETURN (u.format1.apply (c));
+    default:return TRACE_RETURN (false);
     }
   }
 
@@ -1175,17 +1152,14 @@ struct MarkMarkPosFormat1
   {
     TRACE_APPLY ();
     unsigned int mark1_index = (this+mark1Coverage) (c->buffer->info[c->buffer->idx].codepoint);
-    if (likely (mark1_index == NOT_COVERED))
-      return false;
+    if (likely (mark1_index == NOT_COVERED)) return TRACE_RETURN (false);
 
     /* now we search backwards for a suitable mark glyph until a non-mark glyph */
     unsigned int property;
     hb_apply_context_t::mark_skipping_backward_iterator_t skippy_iter (c, c->buffer->idx, 1);
-    if (!skippy_iter.prev (&property))
-      return false;
+    if (!skippy_iter.prev (&property)) return TRACE_RETURN (false);
 
-    if (!(property & HB_OT_LAYOUT_GLYPH_CLASS_MARK))
-      return false;
+    if (!(property & HB_OT_LAYOUT_GLYPH_CLASS_MARK)) return TRACE_RETURN (false);
 
     unsigned int j = skippy_iter.idx;
 
@@ -1195,13 +1169,12 @@ struct MarkMarkPosFormat1
     if ((get_lig_comp (c->buffer->info[j]) != get_lig_comp (c->buffer->info[c->buffer->idx])) ||
 	(get_lig_comp (c->buffer->info[j]) > 0 &&
 	 get_lig_id (c->buffer->info[j]) != get_lig_id (c->buffer->info[c->buffer->idx])))
-      return false;
+      return TRACE_RETURN (false);
 
     unsigned int mark2_index = (this+mark2Coverage) (c->buffer->info[j].codepoint);
-    if (mark2_index == NOT_COVERED)
-      return false;
+    if (mark2_index == NOT_COVERED) return TRACE_RETURN (false);
 
-    return (this+mark1Array).apply (c, mark1_index, mark2_index, this+mark2Array, classCount, j);
+    return TRACE_RETURN ((this+mark1Array).apply (c, mark1_index, mark2_index, this+mark2Array, classCount, j));
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
@@ -1241,8 +1214,8 @@ struct MarkMarkPos
   {
     TRACE_APPLY ();
     switch (u.format) {
-    case 1: return u.format1.apply (c);
-    default:return false;
+    case 1: return TRACE_RETURN (u.format1.apply (c));
+    default:return TRACE_RETURN (false);
     }
   }
 
@@ -1273,7 +1246,7 @@ struct ContextPos : Context
   inline bool apply (hb_apply_context_t *c) const
   {
     TRACE_APPLY ();
-    return Context::apply (c, position_lookup);
+    return TRACE_RETURN (Context::apply (c, position_lookup));
   }
 };
 
@@ -1285,7 +1258,7 @@ struct ChainContextPos : ChainContext
   inline bool apply (hb_apply_context_t *c) const
   {
     TRACE_APPLY ();
-    return ChainContext::apply (c, position_lookup);
+    return TRACE_RETURN (ChainContext::apply (c, position_lookup));
   }
 };
 
@@ -1334,16 +1307,16 @@ struct PosLookupSubTable
   {
     TRACE_APPLY ();
     switch (lookup_type) {
-    case Single:		return u.single.apply (c);
-    case Pair:			return u.pair.apply (c);
-    case Cursive:		return u.cursive.apply (c);
-    case MarkBase:		return u.markBase.apply (c);
-    case MarkLig:		return u.markLig.apply (c);
-    case MarkMark:		return u.markMark.apply (c);
-    case Context:		return u.c.apply (c);
-    case ChainContext:		return u.chainContext.apply (c);
-    case Extension:		return u.extension.apply (c);
-    default:			return false;
+    case Single:		return TRACE_RETURN (u.single.apply (c));
+    case Pair:			return TRACE_RETURN (u.pair.apply (c));
+    case Cursive:		return TRACE_RETURN (u.cursive.apply (c));
+    case MarkBase:		return TRACE_RETURN (u.markBase.apply (c));
+    case MarkLig:		return TRACE_RETURN (u.markLig.apply (c));
+    case MarkMark:		return TRACE_RETURN (u.markMark.apply (c));
+    case Context:		return TRACE_RETURN (u.c.apply (c));
+    case ChainContext:		return TRACE_RETURN (u.chainContext.apply (c));
+    case Extension:		return TRACE_RETURN (u.extension.apply (c));
+    default:			return TRACE_RETURN (false);
     }
   }
 
@@ -1538,7 +1511,7 @@ GPOS::position_finish (hb_buffer_t *buffer)
 inline bool ExtensionPos::apply (hb_apply_context_t *c) const
 {
   TRACE_APPLY ();
-  return get_subtable ().apply (c, get_type ());
+  return TRACE_RETURN (get_subtable ().apply (c, get_type ()));
 }
 
 inline bool ExtensionPos::sanitize (hb_sanitize_context_t *c)
diff --git a/src/hb-ot-layout-gsub-table.hh b/src/hb-ot-layout-gsub-table.hh
index 7bbf82a..682303a 100644
--- a/src/hb-ot-layout-gsub-table.hh
+++ b/src/hb-ot-layout-gsub-table.hh
@@ -60,15 +60,14 @@ struct SingleSubstFormat1
     TRACE_APPLY ();
     hb_codepoint_t glyph_id = c->buffer->info[c->buffer->idx].codepoint;
     unsigned int index = (this+coverage) (glyph_id);
-    if (likely (index == NOT_COVERED))
-      return false;
+    if (likely (index == NOT_COVERED)) return TRACE_RETURN (false);
 
     /* According to the Adobe Annotated OpenType Suite, result is always
      * limited to 16bit. */
     glyph_id = (glyph_id + deltaGlyphID) & 0xFFFF;
     c->replace_glyph (glyph_id);
 
-    return true;
+    return TRACE_RETURN (true);
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
@@ -113,16 +112,14 @@ struct SingleSubstFormat2
     TRACE_APPLY ();
     hb_codepoint_t glyph_id = c->buffer->info[c->buffer->idx].codepoint;
     unsigned int index = (this+coverage) (glyph_id);
-    if (likely (index == NOT_COVERED))
-      return false;
+    if (likely (index == NOT_COVERED)) return TRACE_RETURN (false);
 
-    if (unlikely (index >= substitute.len))
-      return false;
+    if (unlikely (index >= substitute.len)) return TRACE_RETURN (false);
 
     glyph_id = substitute[index];
     c->replace_glyph (glyph_id);
 
-    return true;
+    return TRACE_RETURN (true);
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
@@ -171,9 +168,9 @@ struct SingleSubst
   {
     TRACE_APPLY ();
     switch (u.format) {
-    case 1: return u.format1.apply (c);
-    case 2: return u.format2.apply (c);
-    default:return false;
+    case 1: return TRACE_RETURN (u.format1.apply (c));
+    case 2: return TRACE_RETURN (u.format2.apply (c));
+    default:return TRACE_RETURN (false);
     }
   }
 
@@ -213,14 +210,13 @@ struct Sequence
   inline bool apply (hb_apply_context_t *c) const
   {
     TRACE_APPLY ();
-    if (unlikely (!substitute.len))
-      return false;
+    if (unlikely (!substitute.len)) return TRACE_RETURN (false);
 
     if (c->property & HB_OT_LAYOUT_GLYPH_CLASS_LIGATURE)
       c->guess_glyph_class (HB_OT_LAYOUT_GLYPH_CLASS_BASE_GLYPH);
     c->replace_glyphs_be16 (1, substitute.len, (const uint16_t *) substitute.array);
 
-    return true;
+    return TRACE_RETURN (true);
   }
 
   public:
@@ -262,10 +258,9 @@ struct MultipleSubstFormat1
     TRACE_APPLY ();
 
     unsigned int index = (this+coverage) (c->buffer->info[c->buffer->idx].codepoint);
-    if (likely (index == NOT_COVERED))
-      return false;
+    if (likely (index == NOT_COVERED)) return TRACE_RETURN (false);
 
-    return (this+sequence[index]).apply (c);
+    return TRACE_RETURN ((this+sequence[index]).apply (c));
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
@@ -312,8 +307,8 @@ struct MultipleSubst
   {
     TRACE_APPLY ();
     switch (u.format) {
-    case 1: return u.format1.apply (c);
-    default:return false;
+    case 1: return TRACE_RETURN (u.format1.apply (c));
+    default:return TRACE_RETURN (false);
     }
   }
 
@@ -368,13 +363,11 @@ struct AlternateSubstFormat1
     hb_codepoint_t glyph_id = c->buffer->info[c->buffer->idx].codepoint;
 
     unsigned int index = (this+coverage) (glyph_id);
-    if (likely (index == NOT_COVERED))
-      return false;
+    if (likely (index == NOT_COVERED)) return TRACE_RETURN (false);
 
     const AlternateSet &alt_set = this+alternateSet[index];
 
-    if (unlikely (!alt_set.len))
-      return false;
+    if (unlikely (!alt_set.len)) return TRACE_RETURN (false);
 
     hb_mask_t glyph_mask = c->buffer->info[c->buffer->idx].mask;
     hb_mask_t lookup_mask = c->lookup_mask;
@@ -383,14 +376,13 @@ struct AlternateSubstFormat1
     unsigned int shift = _hb_ctz (lookup_mask);
     unsigned int alt_index = ((lookup_mask & glyph_mask) >> shift);
 
-    if (unlikely (alt_index > alt_set.len || alt_index == 0))
-      return false;
+    if (unlikely (alt_index > alt_set.len || alt_index == 0)) return TRACE_RETURN (false);
 
     glyph_id = alt_set[alt_index - 1];
 
     c->replace_glyph (glyph_id);
 
-    return true;
+    return TRACE_RETURN (true);
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
@@ -437,8 +429,8 @@ struct AlternateSubst
   {
     TRACE_APPLY ();
     switch (u.format) {
-    case 1: return u.format1.apply (c);
-    default:return false;
+    case 1: return TRACE_RETURN (u.format1.apply (c));
+    default:return TRACE_RETURN (false);
     }
   }
 
@@ -484,12 +476,10 @@ struct Ligature
   {
     TRACE_APPLY ();
     unsigned int count = component.len;
-    if (unlikely (count < 2))
-      return false;
+    if (unlikely (count < 2)) return TRACE_RETURN (false);
 
     hb_apply_context_t::mark_skipping_forward_iterator_t skippy_iter (c, c->buffer->idx, count - 1);
-    if (skippy_iter.has_no_chance ())
-      return false;
+    if (skippy_iter.has_no_chance ()) return TRACE_RETURN (false);
 
     bool first_was_mark = (c->property & HB_OT_LAYOUT_GLYPH_CLASS_MARK);
     bool found_non_mark = false;
@@ -498,13 +488,11 @@ struct Ligature
     {
       unsigned int property;
 
-      if (!skippy_iter.next (&property))
-	return false;
+      if (!skippy_iter.next (&property)) return TRACE_RETURN (false);
 
       found_non_mark |= !(property & HB_OT_LAYOUT_GLYPH_CLASS_MARK);
 
-      if (likely (c->buffer->info[skippy_iter.idx].codepoint != component[i]))
-        return false;
+      if (likely (c->buffer->info[skippy_iter.idx].codepoint != component[i])) return TRACE_RETURN (false);
     }
 
     if (first_was_mark && found_non_mark)
@@ -542,7 +530,7 @@ struct Ligature
       }
     }
 
-    return true;
+    return TRACE_RETURN (true);
   }
 
   public:
@@ -594,11 +582,10 @@ struct LigatureSet
     for (unsigned int i = 0; i < num_ligs; i++)
     {
       const Ligature &lig = this+ligature[i];
-      if (lig.apply (c))
-        return true;
+      if (lig.apply (c)) return TRACE_RETURN (true);
     }
 
-    return false;
+    return TRACE_RETURN (false);
   }
 
   public:
@@ -644,11 +631,10 @@ struct LigatureSubstFormat1
     hb_codepoint_t glyph_id = c->buffer->info[c->buffer->idx].codepoint;
 
     unsigned int index = (this+coverage) (glyph_id);
-    if (likely (index == NOT_COVERED))
-      return false;
+    if (likely (index == NOT_COVERED)) return TRACE_RETURN (false);
 
     const LigatureSet &lig_set = this+ligatureSet[index];
-    return lig_set.apply (c);
+    return TRACE_RETURN (lig_set.apply (c));
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
@@ -695,8 +681,8 @@ struct LigatureSubst
   {
     TRACE_APPLY ();
     switch (u.format) {
-    case 1: return u.format1.apply (c);
-    default:return false;
+    case 1: return TRACE_RETURN (u.format1.apply (c));
+    default:return TRACE_RETURN (false);
     }
   }
 
@@ -735,7 +721,7 @@ struct ContextSubst : Context
   inline bool apply (hb_apply_context_t *c) const
   {
     TRACE_APPLY ();
-    return Context::apply (c, substitute_lookup);
+    return TRACE_RETURN (Context::apply (c, substitute_lookup));
   }
 };
 
@@ -754,7 +740,7 @@ struct ChainContextSubst : ChainContext
   inline bool apply (hb_apply_context_t *c) const
   {
     TRACE_APPLY ();
-    return ChainContext::apply (c, substitute_lookup);
+    return TRACE_RETURN (ChainContext::apply (c, substitute_lookup));
   }
 };
 
@@ -818,12 +804,10 @@ struct ReverseChainSingleSubstFormat1
   inline bool apply (hb_apply_context_t *c) const
   {
     TRACE_APPLY ();
-    if (unlikely (c->context_length != NO_CONTEXT))
-      return false; /* No chaining to this type */
+    if (unlikely (c->context_length != NO_CONTEXT)) return TRACE_RETURN (false); /* No chaining to this type */
 
     unsigned int index = (this+coverage) (c->buffer->info[c->buffer->idx].codepoint);
-    if (likely (index == NOT_COVERED))
-      return false;
+    if (likely (index == NOT_COVERED)) return TRACE_RETURN (false);
 
     const OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage> > (backtrack);
     const ArrayOf<GlyphID> &substitute = StructAfter<ArrayOf<GlyphID> > (lookahead);
@@ -838,10 +822,10 @@ struct ReverseChainSingleSubstFormat1
     {
       c->buffer->info[c->buffer->idx].codepoint = substitute[index];
       c->buffer->idx--; /* Reverse! */
-      return true;
+      return TRACE_RETURN (true);
     }
 
-    return false;
+    return TRACE_RETURN (false);
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
@@ -894,8 +878,8 @@ struct ReverseChainSingleSubst
   {
     TRACE_APPLY ();
     switch (u.format) {
-    case 1: return u.format1.apply (c);
-    default:return false;
+    case 1: return TRACE_RETURN (u.format1.apply (c));
+    default:return TRACE_RETURN (false);
     }
   }
 
@@ -979,15 +963,15 @@ struct SubstLookupSubTable
   {
     TRACE_APPLY ();
     switch (lookup_type) {
-    case Single:		return u.single.apply (c);
-    case Multiple:		return u.multiple.apply (c);
-    case Alternate:		return u.alternate.apply (c);
-    case Ligature:		return u.ligature.apply (c);
-    case Context:		return u.c.apply (c);
-    case ChainContext:		return u.chainContext.apply (c);
-    case Extension:		return u.extension.apply (c);
-    case ReverseChainSingle:	return u.reverseChainContextSingle.apply (c);
-    default:			return false;
+    case Single:		return TRACE_RETURN (u.single.apply (c));
+    case Multiple:		return TRACE_RETURN (u.multiple.apply (c));
+    case Alternate:		return TRACE_RETURN (u.alternate.apply (c));
+    case Ligature:		return TRACE_RETURN (u.ligature.apply (c));
+    case Context:		return TRACE_RETURN (u.c.apply (c));
+    case ChainContext:		return TRACE_RETURN (u.chainContext.apply (c));
+    case Extension:		return TRACE_RETURN (u.extension.apply (c));
+    case ReverseChainSingle:	return TRACE_RETURN (u.reverseChainContextSingle.apply (c));
+    default:			return TRACE_RETURN (false);
     }
   }
 
@@ -1217,7 +1201,7 @@ inline bool ExtensionSubst::would_apply (hb_codepoint_t first, hb_codepoint_t se
 inline bool ExtensionSubst::apply (hb_apply_context_t *c) const
 {
   TRACE_APPLY ();
-  return get_subtable ().apply (c, get_type ());
+  return TRACE_RETURN (get_subtable ().apply (c, get_type ()));
 }
 
 inline bool ExtensionSubst::sanitize (hb_sanitize_context_t *c)
diff --git a/src/hb-ot-layout-gsubgpos-private.hh b/src/hb-ot-layout-gsubgpos-private.hh
index 136fff2..c9abd51 100644
--- a/src/hb-ot-layout-gsubgpos-private.hh
+++ b/src/hb-ot-layout-gsubgpos-private.hh
@@ -530,10 +530,7 @@ struct Rule
   {
     TRACE_APPLY ();
     const LookupRecord *lookupRecord = &StructAtOffset<LookupRecord> (input, input[0].static_size * (inputCount ? inputCount - 1 : 0));
-    return context_apply_lookup (c,
-				 inputCount, input,
-				 lookupCount, lookupRecord,
-				 lookup_context);
+    return TRACE_RETURN (context_apply_lookup (c, inputCount, input, lookupCount, lookupRecord, lookup_context));
   }
 
   public:
@@ -576,9 +573,9 @@ struct RuleSet
     for (unsigned int i = 0; i < num_rules; i++)
     {
       if ((this+rule[i]).apply (c, lookup_context))
-        return true;
+        return TRACE_RETURN (true);
     }
-    return false;
+    return TRACE_RETURN (false);
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
@@ -625,14 +622,14 @@ struct ContextFormat1
     TRACE_APPLY ();
     unsigned int index = (this+coverage) (c->buffer->info[c->buffer->idx].codepoint);
     if (likely (index == NOT_COVERED))
-      return false;
+      return TRACE_RETURN (false);
 
     const RuleSet &rule_set = this+ruleSet[index];
     struct ContextApplyLookupContext lookup_context = {
       {match_glyph, apply_func},
       NULL
     };
-    return rule_set.apply (c, lookup_context);
+    return TRACE_RETURN (rule_set.apply (c, lookup_context));
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
@@ -684,8 +681,7 @@ struct ContextFormat2
   {
     TRACE_APPLY ();
     unsigned int index = (this+coverage) (c->buffer->info[c->buffer->idx].codepoint);
-    if (likely (index == NOT_COVERED))
-      return false;
+    if (likely (index == NOT_COVERED)) return TRACE_RETURN (false);
 
     const ClassDef &class_def = this+classDef;
     index = class_def (c->buffer->info[c->buffer->idx].codepoint);
@@ -694,7 +690,7 @@ struct ContextFormat2
       {match_class, apply_func},
       &class_def
     };
-    return rule_set.apply (c, lookup_context);
+    return TRACE_RETURN (rule_set.apply (c, lookup_context));
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
@@ -745,18 +741,14 @@ struct ContextFormat3
   {
     TRACE_APPLY ();
     unsigned int index = (this+coverage[0]) (c->buffer->info[c->buffer->idx].codepoint);
-    if (likely (index == NOT_COVERED))
-      return false;
+    if (likely (index == NOT_COVERED)) return TRACE_RETURN (false);
 
     const LookupRecord *lookupRecord = &StructAtOffset<LookupRecord> (coverage, coverage[0].static_size * glyphCount);
     struct ContextApplyLookupContext lookup_context = {
       {match_coverage, apply_func},
       this
     };
-    return context_apply_lookup (c,
-				 glyphCount, (const USHORT *) (coverage + 1),
-				 lookupCount, lookupRecord,
-				 lookup_context);
+    return TRACE_RETURN (context_apply_lookup (c, glyphCount, (const USHORT *) (coverage + 1), lookupCount, lookupRecord, lookup_context));
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
@@ -803,10 +795,10 @@ struct Context
   {
     TRACE_APPLY ();
     switch (u.format) {
-    case 1: return u.format1.apply (c, apply_func);
-    case 2: return u.format2.apply (c, apply_func);
-    case 3: return u.format3.apply (c, apply_func);
-    default:return false;
+    case 1: return TRACE_RETURN (u.format1.apply (c, apply_func));
+    case 2: return TRACE_RETURN (u.format2.apply (c, apply_func));
+    case 3: return TRACE_RETURN (u.format3.apply (c, apply_func));
+    default:return TRACE_RETURN (false);
     }
   }
 
@@ -931,12 +923,11 @@ struct ChainRule
     const HeadlessArrayOf<USHORT> &input = StructAfter<HeadlessArrayOf<USHORT> > (backtrack);
     const ArrayOf<USHORT> &lookahead = StructAfter<ArrayOf<USHORT> > (input);
     const ArrayOf<LookupRecord> &lookup = StructAfter<ArrayOf<LookupRecord> > (lookahead);
-    return chain_context_apply_lookup (c,
-				       backtrack.len, backtrack.array,
-				       input.len, input.array,
-				       lookahead.len, lookahead.array,
-				       lookup.len, lookup.array,
-				       lookup_context);
+    return TRACE_RETURN (chain_context_apply_lookup (c,
+						     backtrack.len, backtrack.array,
+						     input.len, input.array,
+						     lookahead.len, lookahead.array, lookup.len,
+						     lookup.array, lookup_context));
   }
 
   public:
@@ -984,12 +975,10 @@ struct ChainRuleSet
     TRACE_APPLY ();
     unsigned int num_rules = rule.len;
     for (unsigned int i = 0; i < num_rules; i++)
-    {
       if ((this+rule[i]).apply (c, lookup_context))
-        return true;
-    }
+        return TRACE_RETURN (true);
 
-    return false;
+    return TRACE_RETURN (false);
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
@@ -1033,15 +1022,14 @@ struct ChainContextFormat1
   {
     TRACE_APPLY ();
     unsigned int index = (this+coverage) (c->buffer->info[c->buffer->idx].codepoint);
-    if (likely (index == NOT_COVERED))
-      return false;
+    if (likely (index == NOT_COVERED)) return TRACE_RETURN (false);
 
     const ChainRuleSet &rule_set = this+ruleSet[index];
     struct ChainContextApplyLookupContext lookup_context = {
       {match_glyph, apply_func},
       {NULL, NULL, NULL}
     };
-    return rule_set.apply (c, lookup_context);
+    return TRACE_RETURN (rule_set.apply (c, lookup_context));
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
@@ -1096,8 +1084,7 @@ struct ChainContextFormat2
   {
     TRACE_APPLY ();
     unsigned int index = (this+coverage) (c->buffer->info[c->buffer->idx].codepoint);
-    if (likely (index == NOT_COVERED))
-      return false;
+    if (likely (index == NOT_COVERED)) return TRACE_RETURN (false);
 
     const ClassDef &backtrack_class_def = this+backtrackClassDef;
     const ClassDef &input_class_def = this+inputClassDef;
@@ -1111,7 +1098,7 @@ struct ChainContextFormat2
        &input_class_def,
        &lookahead_class_def}
     };
-    return rule_set.apply (c, lookup_context);
+    return TRACE_RETURN (rule_set.apply (c, lookup_context));
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
@@ -1179,8 +1166,7 @@ struct ChainContextFormat3
     const OffsetArrayOf<Coverage> &input = StructAfter<OffsetArrayOf<Coverage> > (backtrack);
 
     unsigned int index = (this+input[0]) (c->buffer->info[c->buffer->idx].codepoint);
-    if (likely (index == NOT_COVERED))
-      return false;
+    if (likely (index == NOT_COVERED)) return TRACE_RETURN (false);
 
     const OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage> > (input);
     const ArrayOf<LookupRecord> &lookup = StructAfter<ArrayOf<LookupRecord> > (lookahead);
@@ -1188,12 +1174,11 @@ struct ChainContextFormat3
       {match_coverage, apply_func},
       {this, this, this}
     };
-    return chain_context_apply_lookup (c,
-				       backtrack.len, (const USHORT *) backtrack.array,
-				       input.len, (const USHORT *) input.array + 1,
-				       lookahead.len, (const USHORT *) lookahead.array,
-				       lookup.len, lookup.array,
-				       lookup_context);
+    return TRACE_RETURN (chain_context_apply_lookup (c,
+						     backtrack.len, (const USHORT *) backtrack.array,
+						     input.len, (const USHORT *) input.array + 1,
+						     lookahead.len, (const USHORT *) lookahead.array,
+						     lookup.len, lookup.array, lookup_context));
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
@@ -1247,10 +1232,10 @@ struct ChainContext
   {
     TRACE_APPLY ();
     switch (u.format) {
-    case 1: return u.format1.apply (c, apply_func);
-    case 2: return u.format2.apply (c, apply_func);
-    case 3: return u.format3.apply (c, apply_func);
-    default:return false;
+    case 1: return TRACE_RETURN (u.format1.apply (c, apply_func));
+    case 2: return TRACE_RETURN (u.format2.apply (c, apply_func));
+    case 3: return TRACE_RETURN (u.format3.apply (c, apply_func));
+    default:return TRACE_RETURN (false);
     }
   }
 
commit 5ccfe8e2154ad0b58dabcc236bbe9478c17b02ab
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Fri May 11 02:19:41 2012 +0200

    /Minor/

diff --git a/src/hb-private.hh b/src/hb-private.hh
index cac1608..dd2c465 100644
--- a/src/hb-private.hh
+++ b/src/hb-private.hh
@@ -519,7 +519,7 @@ _hb_debug_msg_va (const char *what,
   fprintf (stderr, "%-10s", what ? what : "");
 
   if (obj)
-    fprintf (stderr, "(%0*x) ", (unsigned int) (2 * sizeof (void *)), (unsigned long) obj);
+    fprintf (stderr, "(%0*lx) ", (unsigned int) (2 * sizeof (void *)), (unsigned long) obj);
   else
     fprintf (stderr, " %*s  ", (unsigned int) (2 * sizeof (void *)), "");
 
commit 0ab8c8621712d33e1e91dfdb4ad0b335e3d2a3fb
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Fri May 11 01:25:34 2012 +0200

    Annotate SANITIZE return values
    
    More to come, for APPLY, CLOSURE, etc.

diff --git a/src/hb-open-file-private.hh b/src/hb-open-file-private.hh
index 62e1f17..ce18580 100644
--- a/src/hb-open-file-private.hh
+++ b/src/hb-open-file-private.hh
@@ -1,5 +1,6 @@
 /*
  * Copyright © 2007,2008,2009  Red Hat, Inc.
+ * Copyright © 2012  Google, Inc.
  *
  *  This is part of HarfBuzz, a text shaping library.
  *
@@ -22,6 +23,7 @@
  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
  *
  * Red Hat Author(s): Behdad Esfahbod
+ * Google Author(s): Behdad Esfahbod
  */
 
 #ifndef HB_OPEN_FILE_PRIVATE_HH
@@ -51,7 +53,7 @@ typedef struct TableRecord
 {
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    return c->check_struct (this);
+    return TRACE_RETURN (c->check_struct (this));
   }
 
   Tag		tag;		/* 4-byte identifier. */
@@ -100,8 +102,7 @@ typedef struct OffsetTable
   public:
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    return c->check_struct (this)
-	&& c->check_array (tables, TableRecord::static_size, numTables);
+    return TRACE_RETURN (c->check_struct (this) && c->check_array (tables, TableRecord::static_size, numTables));
   }
 
   private:
@@ -129,7 +130,7 @@ struct TTCHeaderVersion1
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    return table.sanitize (c, this);
+    return TRACE_RETURN (table.sanitize (c, this));
   }
 
   private:
@@ -168,11 +169,11 @@ struct TTCHeader
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    if (unlikely (!u.header.version.sanitize (c))) return false;
+    if (unlikely (!u.header.version.sanitize (c))) return TRACE_RETURN (false);
     switch (u.header.version.major) {
     case 2: /* version 2 is compatible with version 1 */
-    case 1: return u.version1.sanitize (c);
-    default:return true;
+    case 1: return TRACE_RETURN (u.version1.sanitize (c));
+    default:return TRACE_RETURN (true);
     }
   }
 
@@ -230,14 +231,14 @@ struct OpenTypeFontFile
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    if (unlikely (!u.tag.sanitize (c))) return false;
+    if (unlikely (!u.tag.sanitize (c))) return TRACE_RETURN (false);
     switch (u.tag) {
     case CFFTag:	/* All the non-collection tags */
     case TrueTag:
     case Typ1Tag:
-    case TrueTypeTag:	return u.fontFace.sanitize (c);
-    case TTCTag:	return u.ttcHeader.sanitize (c);
-    default:		return true;
+    case TrueTypeTag:	return TRACE_RETURN (u.fontFace.sanitize (c));
+    case TTCTag:	return TRACE_RETURN (u.ttcHeader.sanitize (c));
+    default:		return TRACE_RETURN (true);
     }
   }
 
diff --git a/src/hb-open-type-private.hh b/src/hb-open-type-private.hh
index 25475c5..5bd9827 100644
--- a/src/hb-open-type-private.hh
+++ b/src/hb-open-type-private.hh
@@ -1,5 +1,6 @@
 /*
  * Copyright © 2007,2008,2009,2010  Red Hat, Inc.
+ * Copyright © 2012  Google, Inc.
  *
  *  This is part of HarfBuzz, a text shaping library.
  *
@@ -22,6 +23,7 @@
  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
  *
  * Red Hat Author(s): Behdad Esfahbod
+ * Google Author(s): Behdad Esfahbod
  */
 
 #ifndef HB_OPEN_TYPE_PRIVATE_HH
@@ -153,7 +155,7 @@ ASSERT_STATIC (Type::min_size + 1 <= sizeof (_Null##Type))
 
 
 #define TRACE_SANITIZE() \
-	hb_auto_trace_t<HB_DEBUG_SANITIZE, unsigned int> trace (&c->debug_depth, "SANITIZE", this, HB_FUNC, "");
+	hb_auto_trace_t<HB_DEBUG_SANITIZE> trace (&c->debug_depth, "SANITIZE", this, HB_FUNC, "");
 
 
 struct hb_sanitize_context_t
@@ -371,7 +373,7 @@ struct IntType
   inline int cmp (Type a) const { Type b = v; return a < b ? -1 : a == b ? 0 : +1; }
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    return likely (c->check_struct (this));
+    return TRACE_RETURN (likely (c->check_struct (this)));
   }
   protected:
   BEInt<Type, sizeof (Type)> v;
@@ -401,7 +403,7 @@ struct LONGDATETIME
 {
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    return likely (c->check_struct (this));
+    return TRACE_RETURN (likely (c->check_struct (this)));
   }
   private:
   LONG major;
@@ -465,7 +467,7 @@ struct FixedVersion
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    return c->check_struct (this);
+    return TRACE_RETURN (c->check_struct (this));
   }
 
   USHORT major;
@@ -493,20 +495,20 @@ struct GenericOffsetTo : OffsetType
 
   inline bool sanitize (hb_sanitize_context_t *c, void *base) {
     TRACE_SANITIZE ();
-    if (unlikely (!c->check_struct (this))) return false;
+    if (unlikely (!c->check_struct (this))) return TRACE_RETURN (false);
     unsigned int offset = *this;
-    if (unlikely (!offset)) return true;
+    if (unlikely (!offset)) return TRACE_RETURN (true);
     Type &obj = StructAtOffset<Type> (base, offset);
-    return likely (obj.sanitize (c)) || neuter (c);
+    return TRACE_RETURN (likely (obj.sanitize (c)) || neuter (c));
   }
   template <typename T>
   inline bool sanitize (hb_sanitize_context_t *c, void *base, T user_data) {
     TRACE_SANITIZE ();
-    if (unlikely (!c->check_struct (this))) return false;
+    if (unlikely (!c->check_struct (this))) return TRACE_RETURN (false);
     unsigned int offset = *this;
-    if (unlikely (!offset)) return true;
+    if (unlikely (!offset)) return TRACE_RETURN (true);
     Type &obj = StructAtOffset<Type> (base, offset);
-    return likely (obj.sanitize (c, user_data)) || neuter (c);
+    return TRACE_RETURN (likely (obj.sanitize (c, user_data)) || neuter (c));
   }
 
   private:
@@ -558,7 +560,7 @@ struct GenericArrayOf
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    if (unlikely (!sanitize_shallow (c))) return false;
+    if (unlikely (!sanitize_shallow (c))) return TRACE_RETURN (false);
 
     /* Note: for structs that do not reference other structs,
      * we do not need to call their sanitize() as we already did
@@ -569,33 +571,32 @@ struct GenericArrayOf
      */
     (void) (false && array[0].sanitize (c));
 
-    return true;
+    return TRACE_RETURN (true);
   }
   inline bool sanitize (hb_sanitize_context_t *c, void *base) {
     TRACE_SANITIZE ();
-    if (unlikely (!sanitize_shallow (c))) return false;
+    if (unlikely (!sanitize_shallow (c))) return TRACE_RETURN (false);
     unsigned int count = len;
     for (unsigned int i = 0; i < count; i++)
       if (unlikely (!array[i].sanitize (c, base)))
-        return false;
-    return true;
+        return TRACE_RETURN (false);
+    return TRACE_RETURN (true);
   }
   template <typename T>
   inline bool sanitize (hb_sanitize_context_t *c, void *base, T user_data) {
     TRACE_SANITIZE ();
-    if (unlikely (!sanitize_shallow (c))) return false;
+    if (unlikely (!sanitize_shallow (c))) return TRACE_RETURN (false);
     unsigned int count = len;
     for (unsigned int i = 0; i < count; i++)
       if (unlikely (!array[i].sanitize (c, base, user_data)))
-        return false;
-    return true;
+        return TRACE_RETURN (false);
+    return TRACE_RETURN (true);
   }
 
   private:
   inline bool sanitize_shallow (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    return c->check_struct (this)
-	&& c->check_array (this, Type::static_size, len);
+    return TRACE_RETURN (c->check_struct (this) && c->check_array (this, Type::static_size, len));
   }
 
   public:
@@ -637,12 +638,12 @@ struct OffsetListOf : OffsetArrayOf<Type>
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    return OffsetArrayOf<Type>::sanitize (c, this);
+    return TRACE_RETURN (OffsetArrayOf<Type>::sanitize (c, this));
   }
   template <typename T>
   inline bool sanitize (hb_sanitize_context_t *c, T user_data) {
     TRACE_SANITIZE ();
-    return OffsetArrayOf<Type>::sanitize (c, this, user_data);
+    return TRACE_RETURN (OffsetArrayOf<Type>::sanitize (c, this, user_data));
   }
 };
 
@@ -667,7 +668,7 @@ struct HeadlessArrayOf
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    if (unlikely (!sanitize_shallow (c))) return false;
+    if (unlikely (!sanitize_shallow (c))) return TRACE_RETURN (false);
 
     /* Note: for structs that do not reference other structs,
      * we do not need to call their sanitize() as we already did
@@ -678,7 +679,7 @@ struct HeadlessArrayOf
      */
     (void) (false && array[0].sanitize (c));
 
-    return true;
+    return TRACE_RETURN (true);
   }
 
   USHORT len;
diff --git a/src/hb-ot-head-table.hh b/src/hb-ot-head-table.hh
index a4d8d5f..32d64ca 100644
--- a/src/hb-ot-head-table.hh
+++ b/src/hb-ot-head-table.hh
@@ -1,5 +1,6 @@
 /*
  * Copyright © 2010  Red Hat, Inc.
+ * Copyright © 2012  Google, Inc.
  *
  *  This is part of HarfBuzz, a text shaping library.
  *
@@ -22,6 +23,7 @@
  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
  *
  * Red Hat Author(s): Behdad Esfahbod
+ * Google Author(s): Behdad Esfahbod
  */
 
 #ifndef HB_OT_HEAD_TABLE_HH
@@ -49,7 +51,7 @@ struct head
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    return c->check_struct (this) && likely (version.major == 1);
+    return TRACE_RETURN (c->check_struct (this) && likely (version.major == 1));
   }
 
   private:
diff --git a/src/hb-ot-hhea-table.hh b/src/hb-ot-hhea-table.hh
index 34bf494..2eea05a 100644
--- a/src/hb-ot-hhea-table.hh
+++ b/src/hb-ot-hhea-table.hh
@@ -1,5 +1,5 @@
 /*
- * Copyright © 2011  Google, Inc.
+ * Copyright © 2011,2012  Google, Inc.
  *
  *  This is part of HarfBuzz, a text shaping library.
  *
@@ -44,7 +44,7 @@ struct hhea
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    return c->check_struct (this) && likely (version.major == 1);
+    return TRACE_RETURN (c->check_struct (this) && likely (version.major == 1));
   }
 
   private:
diff --git a/src/hb-ot-hmtx-table.hh b/src/hb-ot-hmtx-table.hh
index b58799c..35cfb48 100644
--- a/src/hb-ot-hmtx-table.hh
+++ b/src/hb-ot-hmtx-table.hh
@@ -1,5 +1,5 @@
 /*
- * Copyright © 2011  Google, Inc.
+ * Copyright © 2011,2012  Google, Inc.
  *
  *  This is part of HarfBuzz, a text shaping library.
  *
@@ -54,7 +54,7 @@ struct hmtx
     TRACE_SANITIZE ();
     /* We don't check for anything specific here.  The users of the
      * struct do all the hard work... */
-    return true;
+    return TRACE_RETURN (true);
   }
 
   private:
diff --git a/src/hb-ot-layout-common-private.hh b/src/hb-ot-layout-common-private.hh
index 142eb5c..2051000 100644
--- a/src/hb-ot-layout-common-private.hh
+++ b/src/hb-ot-layout-common-private.hh
@@ -60,8 +60,7 @@ struct Record
 
   inline bool sanitize (hb_sanitize_context_t *c, void *base) {
     TRACE_SANITIZE ();
-    return c->check_struct (this)
-	&& offset.sanitize (c, base);
+    return TRACE_RETURN (c->check_struct (this) && offset.sanitize (c, base));
   }
 
   Tag		tag;		/* 4-byte Tag identifier */
@@ -115,7 +114,7 @@ struct RecordListOf : RecordArrayOf<Type>
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    return RecordArrayOf<Type>::sanitize (c, this);
+    return TRACE_RETURN (RecordArrayOf<Type>::sanitize (c, this));
   }
 };
 
@@ -129,7 +128,7 @@ struct RangeRecord
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    return c->check_struct (this);
+    return TRACE_RETURN (c->check_struct (this));
   }
 
   inline bool intersects (const hb_set_t *glyphs) const {
@@ -188,8 +187,7 @@ struct LangSys
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    return c->check_struct (this)
-	&& featureIndex.sanitize (c);
+    return TRACE_RETURN (c->check_struct (this) && featureIndex.sanitize (c));
   }
 
   Offset	lookupOrder;	/* = Null (reserved for an offset to a
@@ -227,8 +225,7 @@ struct Script
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    return defaultLangSys.sanitize (c, this)
-	&& langSys.sanitize (c, this);
+    return TRACE_RETURN (defaultLangSys.sanitize (c, this) && langSys.sanitize (c, this));
   }
 
   private:
@@ -258,8 +255,7 @@ struct Feature
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    return c->check_struct (this)
-	&& lookupIndex.sanitize (c);
+    return TRACE_RETURN (c->check_struct (this) && lookupIndex.sanitize (c));
   }
 
   Offset	featureParams;	/* Offset to Feature Parameters table (if one
@@ -313,14 +309,13 @@ struct Lookup
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
     /* Real sanitize of the subtables is done by GSUB/GPOS/... */
-    if (!(c->check_struct (this)
-       && subTable.sanitize (c))) return false;
+    if (!(c->check_struct (this) && subTable.sanitize (c))) return TRACE_RETURN (false);
     if (unlikely (lookupFlag & LookupFlag::UseMarkFilteringSet))
     {
       USHORT &markFilteringSet = StructAfter<USHORT> (subTable);
-      if (!markFilteringSet.sanitize (c)) return false;
+      if (!markFilteringSet.sanitize (c)) return TRACE_RETURN (false);
     }
-    return true;
+    return TRACE_RETURN (true);
   }
 
   USHORT	lookupType;		/* Different enumerations for GSUB and GPOS */
@@ -356,7 +351,7 @@ struct CoverageFormat1
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    return glyphArray.sanitize (c);
+    return TRACE_RETURN (glyphArray.sanitize (c));
   }
 
   inline bool intersects_coverage (const hb_set_t *glyphs, unsigned int index) const {
@@ -400,7 +395,7 @@ struct CoverageFormat2
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    return rangeRecord.sanitize (c);
+    return TRACE_RETURN (rangeRecord.sanitize (c));
   }
 
   inline bool intersects_coverage (const hb_set_t *glyphs, unsigned int index) const {
@@ -469,11 +464,11 @@ struct Coverage
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    if (!u.format.sanitize (c)) return false;
+    if (!u.format.sanitize (c)) return TRACE_RETURN (false);
     switch (u.format) {
-    case 1: return u.format1.sanitize (c);
-    case 2: return u.format2.sanitize (c);
-    default:return true;
+    case 1: return TRACE_RETURN (u.format1.sanitize (c));
+    case 2: return TRACE_RETURN (u.format2.sanitize (c));
+    default:return TRACE_RETURN (true);
     }
   }
 
@@ -571,8 +566,7 @@ struct ClassDefFormat1
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    return c->check_struct (this)
-	&& classValue.sanitize (c);
+    return TRACE_RETURN (c->check_struct (this) && classValue.sanitize (c));
   }
 
   inline bool intersects_class (const hb_set_t *glyphs, unsigned int klass) const {
@@ -606,7 +600,7 @@ struct ClassDefFormat2
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    return rangeRecord.sanitize (c);
+    return TRACE_RETURN (rangeRecord.sanitize (c));
   }
 
   inline bool intersects_class (const hb_set_t *glyphs, unsigned int klass) const {
@@ -640,11 +634,11 @@ struct ClassDef
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    if (!u.format.sanitize (c)) return false;
+    if (!u.format.sanitize (c)) return TRACE_RETURN (false);
     switch (u.format) {
-    case 1: return u.format1.sanitize (c);
-    case 2: return u.format2.sanitize (c);
-    default:return true;
+    case 1: return TRACE_RETURN (u.format1.sanitize (c));
+    case 2: return TRACE_RETURN (u.format2.sanitize (c));
+    default:return TRACE_RETURN (true);
     }
   }
 
@@ -724,8 +718,7 @@ struct Device
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    return c->check_struct (this)
-	&& c->check_range (this, this->get_size ());
+    return TRACE_RETURN (c->check_struct (this) && c->check_range (this, this->get_size ()));
   }
 
   private:
diff --git a/src/hb-ot-layout-gdef-table.hh b/src/hb-ot-layout-gdef-table.hh
index f3f76fa..f29fc14 100644
--- a/src/hb-ot-layout-gdef-table.hh
+++ b/src/hb-ot-layout-gdef-table.hh
@@ -1,6 +1,6 @@
 /*
  * Copyright © 2007,2008,2009  Red Hat, Inc.
- * Copyright © 2010,2011  Google, Inc.
+ * Copyright © 2010,2011,2012  Google, Inc.
  *
  *  This is part of HarfBuzz, a text shaping library.
  *
@@ -71,8 +71,7 @@ struct AttachList
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    return coverage.sanitize (c, this)
-	&& attachPoint.sanitize (c, this);
+    return TRACE_RETURN (coverage.sanitize (c, this) && attachPoint.sanitize (c, this));
   }
 
   private:
@@ -102,7 +101,7 @@ struct CaretValueFormat1
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    return c->check_struct (this);
+    return TRACE_RETURN (c->check_struct (this));
   }
 
   private:
@@ -128,7 +127,7 @@ struct CaretValueFormat2
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    return c->check_struct (this);
+    return TRACE_RETURN (c->check_struct (this));
   }
 
   private:
@@ -151,8 +150,7 @@ struct CaretValueFormat3
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    return c->check_struct (this)
-	&& deviceTable.sanitize (c, this);
+    return TRACE_RETURN (c->check_struct (this) && deviceTable.sanitize (c, this));
   }
 
   private:
@@ -180,12 +178,12 @@ struct CaretValue
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    if (!u.format.sanitize (c)) return false;
+    if (!u.format.sanitize (c)) return TRACE_RETURN (false);
     switch (u.format) {
-    case 1: return u.format1.sanitize (c);
-    case 2: return u.format2.sanitize (c);
-    case 3: return u.format3.sanitize (c);
-    default:return true;
+    case 1: return TRACE_RETURN (u.format1.sanitize (c));
+    case 2: return TRACE_RETURN (u.format2.sanitize (c));
+    case 3: return TRACE_RETURN (u.format3.sanitize (c));
+    default:return TRACE_RETURN (true);
     }
   }
 
@@ -221,7 +219,7 @@ struct LigGlyph
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    return carets.sanitize (c, this);
+    return TRACE_RETURN (carets.sanitize (c, this));
   }
 
   private:
@@ -255,8 +253,7 @@ struct LigCaretList
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    return coverage.sanitize (c, this)
-	&& ligGlyph.sanitize (c, this);
+    return TRACE_RETURN (coverage.sanitize (c, this) && ligGlyph.sanitize (c, this));
   }
 
   private:
@@ -278,7 +275,7 @@ struct MarkGlyphSetsFormat1
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    return coverage.sanitize (c, this);
+    return TRACE_RETURN (coverage.sanitize (c, this));
   }
 
   private:
@@ -302,10 +299,10 @@ struct MarkGlyphSets
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    if (!u.format.sanitize (c)) return false;
+    if (!u.format.sanitize (c)) return TRACE_RETURN (false);
     switch (u.format) {
-    case 1: return u.format1.sanitize (c);
-    default:return true;
+    case 1: return TRACE_RETURN (u.format1.sanitize (c));
+    default:return TRACE_RETURN (true);
     }
   }
 
@@ -365,12 +362,13 @@ struct GDEF
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    return version.sanitize (c) && likely (version.major == 1)
-	&& glyphClassDef.sanitize (c, this)
-	&& attachList.sanitize (c, this)
-	&& ligCaretList.sanitize (c, this)
-	&& markAttachClassDef.sanitize (c, this)
-	&& (version.to_int () < 0x00010002 || markGlyphSetsDef[0].sanitize (c, this));
+    return TRACE_RETURN (version.sanitize (c) &&
+			 likely (version.major == 1) &&
+			 glyphClassDef.sanitize (c, this) &&
+			 attachList.sanitize (c, this) &&
+			 ligCaretList.sanitize (c, this) &&
+			 markAttachClassDef.sanitize (c, this) &&
+			 (version.to_int () < 0x00010002 || markGlyphSetsDef[0].sanitize (c, this)));
   }
 
 
diff --git a/src/hb-ot-layout-gpos-table.hh b/src/hb-ot-layout-gpos-table.hh
index 1b08505..1b3677e 100644
--- a/src/hb-ot-layout-gpos-table.hh
+++ b/src/hb-ot-layout-gpos-table.hh
@@ -171,40 +171,39 @@ struct ValueFormat : USHORT
 
   inline bool sanitize_value (hb_sanitize_context_t *c, void *base, Value *values) {
     TRACE_SANITIZE ();
-    return c->check_range (values, get_size ())
-	&& (!has_device () || sanitize_value_devices (c, base, values));
+    return TRACE_RETURN (c->check_range (values, get_size ()) && (!has_device () || sanitize_value_devices (c, base, values)));
   }
 
   inline bool sanitize_values (hb_sanitize_context_t *c, void *base, Value *values, unsigned int count) {
     TRACE_SANITIZE ();
     unsigned int len = get_len ();
 
-    if (!c->check_array (values, get_size (), count)) return false;
+    if (!c->check_array (values, get_size (), count)) return TRACE_RETURN (false);
 
-    if (!has_device ()) return true;
+    if (!has_device ()) return TRACE_RETURN (true);
 
     for (unsigned int i = 0; i < count; i++) {
       if (!sanitize_value_devices (c, base, values))
-        return false;
+        return TRACE_RETURN (false);
       values += len;
     }
 
-    return true;
+    return TRACE_RETURN (true);
   }
 
   /* Just sanitize referenced Device tables.  Doesn't check the values themselves. */
   inline bool sanitize_values_stride_unsafe (hb_sanitize_context_t *c, void *base, Value *values, unsigned int count, unsigned int stride) {
     TRACE_SANITIZE ();
 
-    if (!has_device ()) return true;
+    if (!has_device ()) return TRACE_RETURN (true);
 
     for (unsigned int i = 0; i < count; i++) {
       if (!sanitize_value_devices (c, base, values))
-        return false;
+        return TRACE_RETURN (false);
       values += stride;
     }
 
-    return true;
+    return TRACE_RETURN (true);
   }
 };
 
@@ -223,7 +222,7 @@ struct AnchorFormat1
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    return c->check_struct (this);
+    return TRACE_RETURN (c->check_struct (this));
   }
 
   private:
@@ -255,7 +254,7 @@ struct AnchorFormat2
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    return c->check_struct (this);
+    return TRACE_RETURN (c->check_struct (this));
   }
 
   private:
@@ -286,9 +285,7 @@ struct AnchorFormat3
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    return c->check_struct (this)
-	&& xDeviceTable.sanitize (c, this)
-	&& yDeviceTable.sanitize (c, this);
+    return TRACE_RETURN (c->check_struct (this) && xDeviceTable.sanitize (c, this) && yDeviceTable.sanitize (c, this));
   }
 
   private:
@@ -323,12 +320,12 @@ struct Anchor
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    if (!u.format.sanitize (c)) return false;
+    if (!u.format.sanitize (c)) return TRACE_RETURN (false);
     switch (u.format) {
-    case 1: return u.format1.sanitize (c);
-    case 2: return u.format2.sanitize (c);
-    case 3: return u.format3.sanitize (c);
-    default:return true;
+    case 1: return TRACE_RETURN (u.format1.sanitize (c));
+    case 2: return TRACE_RETURN (u.format2.sanitize (c));
+    case 3: return TRACE_RETURN (u.format3.sanitize (c));
+    default:return TRACE_RETURN (true);
     }
   }
 
@@ -353,13 +350,13 @@ struct AnchorMatrix
 
   inline bool sanitize (hb_sanitize_context_t *c, unsigned int cols) {
     TRACE_SANITIZE ();
-    if (!c->check_struct (this)) return false;
-    if (unlikely (rows > 0 && cols >= ((unsigned int) -1) / rows)) return false;
+    if (!c->check_struct (this)) return TRACE_RETURN (false);
+    if (unlikely (rows > 0 && cols >= ((unsigned int) -1) / rows)) return TRACE_RETURN (false);
     unsigned int count = rows * cols;
-    if (!c->check_array (matrix, matrix[0].static_size, count)) return false;
+    if (!c->check_array (matrix, matrix[0].static_size, count)) return TRACE_RETURN (false);
     for (unsigned int i = 0; i < count; i++)
-      if (!matrix[i].sanitize (c, this)) return false;
-    return true;
+      if (!matrix[i].sanitize (c, this)) return TRACE_RETURN (false);
+    return TRACE_RETURN (true);
   }
 
   USHORT	rows;			/* Number of rows */
@@ -378,8 +375,7 @@ struct MarkRecord
 
   inline bool sanitize (hb_sanitize_context_t *c, void *base) {
     TRACE_SANITIZE ();
-    return c->check_struct (this)
-	&& markAnchor.sanitize (c, base);
+    return TRACE_RETURN (c->check_struct (this) && markAnchor.sanitize (c, base));
   }
 
   private:
@@ -421,7 +417,7 @@ struct MarkArray : ArrayOf<MarkRecord>	/* Array of MarkRecords--in Coverage orde
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    return ArrayOf<MarkRecord>::sanitize (c, this);
+    return TRACE_RETURN (ArrayOf<MarkRecord>::sanitize (c, this));
   }
 };
 
@@ -449,9 +445,7 @@ struct SinglePosFormat1
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    return c->check_struct (this)
-	&& coverage.sanitize (c, this)
-	&& valueFormat.sanitize_value (c, this, values);
+    return TRACE_RETURN (c->check_struct (this) && coverage.sanitize (c, this) && valueFormat.sanitize_value (c, this, values));
   }
 
   private:
@@ -493,9 +487,7 @@ struct SinglePosFormat2
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    return c->check_struct (this)
-	&& coverage.sanitize (c, this)
-	&& valueFormat.sanitize_values (c, this, values, valueCount);
+    return TRACE_RETURN (c->check_struct (this) && coverage.sanitize (c, this) && valueFormat.sanitize_values (c, this, values, valueCount));
   }
 
   private:
@@ -529,11 +521,11 @@ struct SinglePos
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    if (!u.format.sanitize (c)) return false;
+    if (!u.format.sanitize (c)) return TRACE_RETURN (false);
     switch (u.format) {
-    case 1: return u.format1.sanitize (c);
-    case 2: return u.format2.sanitize (c);
-    default:return true;
+    case 1: return TRACE_RETURN (u.format1.sanitize (c));
+    case 2: return TRACE_RETURN (u.format2.sanitize (c));
+    default:return TRACE_RETURN (true);
     }
   }
 
@@ -604,12 +596,12 @@ struct PairSet
   inline bool sanitize (hb_sanitize_context_t *c, const sanitize_closure_t *closure) {
     TRACE_SANITIZE ();
     if (!(c->check_struct (this)
-       && c->check_array (array, USHORT::static_size * closure->stride, len))) return false;
+       && c->check_array (array, USHORT::static_size * closure->stride, len))) return TRACE_RETURN (false);
 
     unsigned int count = len;
     PairValueRecord *record = CastP<PairValueRecord> (array);
-    return closure->valueFormats[0].sanitize_values_stride_unsafe (c, closure->base, &record->values[0], count, closure->stride)
-	&& closure->valueFormats[1].sanitize_values_stride_unsafe (c, closure->base, &record->values[closure->len1], count, closure->stride);
+    return TRACE_RETURN (closure->valueFormats[0].sanitize_values_stride_unsafe (c, closure->base, &record->values[0], count, closure->stride)
+		      && closure->valueFormats[1].sanitize_values_stride_unsafe (c, closure->base, &record->values[closure->len1], count, closure->stride));
   }
 
   private:
@@ -654,9 +646,7 @@ struct PairPosFormat1
       1 + len1 + len2
     };
 
-    return c->check_struct (this)
-	&& coverage.sanitize (c, this)
-	&& pairSet.sanitize (c, this, &closure);
+    return TRACE_RETURN (c->check_struct (this) && coverage.sanitize (c, this) && pairSet.sanitize (c, this, &closure));
   }
 
   private:
@@ -723,16 +713,16 @@ struct PairPosFormat2
     if (!(c->check_struct (this)
        && coverage.sanitize (c, this)
        && classDef1.sanitize (c, this)
-       && classDef2.sanitize (c, this))) return false;
+       && classDef2.sanitize (c, this))) return TRACE_RETURN (false);
 
     unsigned int len1 = valueFormat1.get_len ();
     unsigned int len2 = valueFormat2.get_len ();
     unsigned int stride = len1 + len2;
     unsigned int record_size = valueFormat1.get_size () + valueFormat2.get_size ();
     unsigned int count = (unsigned int) class1Count * (unsigned int) class2Count;
-    return c->check_array (values, record_size, count) &&
-	   valueFormat1.sanitize_values_stride_unsafe (c, this, &values[0], count, stride) &&
-	   valueFormat2.sanitize_values_stride_unsafe (c, this, &values[len1], count, stride);
+    return TRACE_RETURN (c->check_array (values, record_size, count) &&
+			 valueFormat1.sanitize_values_stride_unsafe (c, this, &values[0], count, stride) &&
+			 valueFormat2.sanitize_values_stride_unsafe (c, this, &values[len1], count, stride));
   }
 
   private:
@@ -782,11 +772,11 @@ struct PairPos
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    if (!u.format.sanitize (c)) return false;
+    if (!u.format.sanitize (c)) return TRACE_RETURN (false);
     switch (u.format) {
-    case 1: return u.format1.sanitize (c);
-    case 2: return u.format2.sanitize (c);
-    default:return true;
+    case 1: return TRACE_RETURN (u.format1.sanitize (c));
+    case 2: return TRACE_RETURN (u.format2.sanitize (c));
+    default:return TRACE_RETURN (true);
     }
   }
 
@@ -805,8 +795,7 @@ struct EntryExitRecord
 
   inline bool sanitize (hb_sanitize_context_t *c, void *base) {
     TRACE_SANITIZE ();
-    return entryAnchor.sanitize (c, base)
-	&& exitAnchor.sanitize (c, base);
+    return TRACE_RETURN (entryAnchor.sanitize (c, base) && exitAnchor.sanitize (c, base));
   }
 
   private:
@@ -916,8 +905,7 @@ struct CursivePosFormat1
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    return coverage.sanitize (c, this)
-	&& entryExitRecord.sanitize (c, this);
+    return TRACE_RETURN (coverage.sanitize (c, this) && entryExitRecord.sanitize (c, this));
   }
 
   private:
@@ -948,10 +936,10 @@ struct CursivePos
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    if (!u.format.sanitize (c)) return false;
+    if (!u.format.sanitize (c)) return TRACE_RETURN (false);
     switch (u.format) {
-    case 1: return u.format1.sanitize (c);
-    default:return true;
+    case 1: return TRACE_RETURN (u.format1.sanitize (c));
+    default:return TRACE_RETURN (true);
     }
   }
 
@@ -999,11 +987,8 @@ struct MarkBasePosFormat1
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    return c->check_struct (this)
-        && markCoverage.sanitize (c, this)
-	&& baseCoverage.sanitize (c, this)
-	&& markArray.sanitize (c, this)
-	&& baseArray.sanitize (c, this, (unsigned int) classCount);
+    return TRACE_RETURN (c->check_struct (this) && markCoverage.sanitize (c, this) && baseCoverage.sanitize (c, this) &&
+			 markArray.sanitize (c, this) && baseArray.sanitize (c, this, (unsigned int) classCount));
   }
 
   private:
@@ -1041,10 +1026,10 @@ struct MarkBasePos
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    if (!u.format.sanitize (c)) return false;
+    if (!u.format.sanitize (c)) return TRACE_RETURN (false);
     switch (u.format) {
-    case 1: return u.format1.sanitize (c);
-    default:return true;
+    case 1: return TRACE_RETURN (u.format1.sanitize (c));
+    default:return TRACE_RETURN (true);
     }
   }
 
@@ -1121,11 +1106,8 @@ struct MarkLigPosFormat1
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    return c->check_struct (this)
-        && markCoverage.sanitize (c, this)
-	&& ligatureCoverage.sanitize (c, this)
-	&& markArray.sanitize (c, this)
-	&& ligatureArray.sanitize (c, this, (unsigned int) classCount);
+    return TRACE_RETURN (c->check_struct (this) && markCoverage.sanitize (c, this) && ligatureCoverage.sanitize (c, this) &&
+			 markArray.sanitize (c, this) && ligatureArray.sanitize (c, this, (unsigned int) classCount));
   }
 
   private:
@@ -1164,10 +1146,10 @@ struct MarkLigPos
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    if (!u.format.sanitize (c)) return false;
+    if (!u.format.sanitize (c)) return TRACE_RETURN (false);
     switch (u.format) {
-    case 1: return u.format1.sanitize (c);
-    default:return true;
+    case 1: return TRACE_RETURN (u.format1.sanitize (c));
+    default:return TRACE_RETURN (true);
     }
   }
 
@@ -1224,11 +1206,9 @@ struct MarkMarkPosFormat1
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    return c->check_struct (this)
-	&& mark1Coverage.sanitize (c, this)
-	&& mark2Coverage.sanitize (c, this)
-	&& mark1Array.sanitize (c, this)
-	&& mark2Array.sanitize (c, this, (unsigned int) classCount);
+    return TRACE_RETURN (c->check_struct (this) && mark1Coverage.sanitize (c, this) &&
+			 mark2Coverage.sanitize (c, this) && mark1Array.sanitize (c, this)
+			 && mark2Array.sanitize (c, this, (unsigned int) classCount));
   }
 
   private:
@@ -1268,10 +1248,10 @@ struct MarkMarkPos
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    if (!u.format.sanitize (c)) return false;
+    if (!u.format.sanitize (c)) return TRACE_RETURN (false);
     switch (u.format) {
-    case 1: return u.format1.sanitize (c);
-    default:return true;
+    case 1: return TRACE_RETURN (u.format1.sanitize (c));
+    default:return TRACE_RETURN (true);
     }
   }
 
@@ -1363,23 +1343,23 @@ struct PosLookupSubTable
     case Context:		return u.c.apply (c);
     case ChainContext:		return u.chainContext.apply (c);
     case Extension:		return u.extension.apply (c);
-    default:return false;
+    default:			return false;
     }
   }
 
   inline bool sanitize (hb_sanitize_context_t *c, unsigned int lookup_type) {
     TRACE_SANITIZE ();
     switch (lookup_type) {
-    case Single:		return u.single.sanitize (c);
-    case Pair:			return u.pair.sanitize (c);
-    case Cursive:		return u.cursive.sanitize (c);
-    case MarkBase:		return u.markBase.sanitize (c);
-    case MarkLig:		return u.markLig.sanitize (c);
-    case MarkMark:		return u.markMark.sanitize (c);
-    case Context:		return u.c.sanitize (c);
-    case ChainContext:		return u.chainContext.sanitize (c);
-    case Extension:		return u.extension.sanitize (c);
-    default:return true;
+    case Single:		return TRACE_RETURN (u.single.sanitize (c));
+    case Pair:			return TRACE_RETURN (u.pair.sanitize (c));
+    case Cursive:		return TRACE_RETURN (u.cursive.sanitize (c));
+    case MarkBase:		return TRACE_RETURN (u.markBase.sanitize (c));
+    case MarkLig:		return TRACE_RETURN (u.markLig.sanitize (c));
+    case MarkMark:		return TRACE_RETURN (u.markMark.sanitize (c));
+    case Context:		return TRACE_RETURN (u.c.sanitize (c));
+    case ChainContext:		return TRACE_RETURN (u.chainContext.sanitize (c));
+    case Extension:		return TRACE_RETURN (u.extension.sanitize (c));
+    default:			return TRACE_RETURN (true);
     }
   }
 
@@ -1443,9 +1423,9 @@ struct PosLookup : Lookup
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    if (unlikely (!Lookup::sanitize (c))) return false;
+    if (unlikely (!Lookup::sanitize (c))) return TRACE_RETURN (false);
     OffsetArrayOf<PosLookupSubTable> &list = CastR<OffsetArrayOf<PosLookupSubTable> > (subTable);
-    return list.sanitize (c, this, get_type ());
+    return TRACE_RETURN (list.sanitize (c, this, get_type ()));
   }
 };
 
@@ -1470,9 +1450,9 @@ struct GPOS : GSUBGPOS
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    if (unlikely (!GSUBGPOS::sanitize (c))) return false;
+    if (unlikely (!GSUBGPOS::sanitize (c))) return TRACE_RETURN (false);
     OffsetTo<PosLookupList> &list = CastR<OffsetTo<PosLookupList> > (lookupList);
-    return list.sanitize (c, this);
+    return TRACE_RETURN (list.sanitize (c, this));
   }
   public:
   DEFINE_SIZE_STATIC (10);
@@ -1564,10 +1544,10 @@ inline bool ExtensionPos::apply (hb_apply_context_t *c) const
 inline bool ExtensionPos::sanitize (hb_sanitize_context_t *c)
 {
   TRACE_SANITIZE ();
-  if (unlikely (!Extension::sanitize (c))) return false;
+  if (unlikely (!Extension::sanitize (c))) return TRACE_RETURN (false);
   unsigned int offset = get_offset ();
-  if (unlikely (!offset)) return true;
-  return StructAtOffset<PosLookupSubTable> (this, offset).sanitize (c, get_type ());
+  if (unlikely (!offset)) return TRACE_RETURN (true);
+  return TRACE_RETURN (StructAtOffset<PosLookupSubTable> (this, offset).sanitize (c, get_type ()));
 }
 
 static inline bool position_lookup (hb_apply_context_t *c, unsigned int lookup_index)
diff --git a/src/hb-ot-layout-gsub-table.hh b/src/hb-ot-layout-gsub-table.hh
index 4c5fbaa..7bbf82a 100644
--- a/src/hb-ot-layout-gsub-table.hh
+++ b/src/hb-ot-layout-gsub-table.hh
@@ -73,8 +73,7 @@ struct SingleSubstFormat1
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    return coverage.sanitize (c, this)
-	&& deltaGlyphID.sanitize (c);
+    return TRACE_RETURN (coverage.sanitize (c, this) && deltaGlyphID.sanitize (c));
   }
 
   private:
@@ -128,8 +127,7 @@ struct SingleSubstFormat2
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    return coverage.sanitize (c, this)
-	&& substitute.sanitize (c);
+    return TRACE_RETURN (coverage.sanitize (c, this) && substitute.sanitize (c));
   }
 
   private:
@@ -181,11 +179,11 @@ struct SingleSubst
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    if (!u.format.sanitize (c)) return false;
+    if (!u.format.sanitize (c)) return TRACE_RETURN (false);
     switch (u.format) {
-    case 1: return u.format1.sanitize (c);
-    case 2: return u.format2.sanitize (c);
-    default:return true;
+    case 1: return TRACE_RETURN (u.format1.sanitize (c));
+    case 2: return TRACE_RETURN (u.format2.sanitize (c));
+    default:return TRACE_RETURN (true);
     }
   }
 
@@ -228,7 +226,7 @@ struct Sequence
   public:
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    return substitute.sanitize (c);
+    return TRACE_RETURN (substitute.sanitize (c));
   }
 
   private:
@@ -272,8 +270,7 @@ struct MultipleSubstFormat1
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    return coverage.sanitize (c, this)
-	&& sequence.sanitize (c, this);
+    return TRACE_RETURN (coverage.sanitize (c, this) && sequence.sanitize (c, this));
   }
 
   private:
@@ -322,10 +319,10 @@ struct MultipleSubst
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    if (!u.format.sanitize (c)) return false;
+    if (!u.format.sanitize (c)) return TRACE_RETURN (false);
     switch (u.format) {
-    case 1: return u.format1.sanitize (c);
-    default:return true;
+    case 1: return TRACE_RETURN (u.format1.sanitize (c));
+    default:return TRACE_RETURN (true);
     }
   }
 
@@ -398,8 +395,7 @@ struct AlternateSubstFormat1
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    return coverage.sanitize (c, this)
-	&& alternateSet.sanitize (c, this);
+    return TRACE_RETURN (coverage.sanitize (c, this) && alternateSet.sanitize (c, this));
   }
 
   private:
@@ -448,10 +444,10 @@ struct AlternateSubst
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    if (!u.format.sanitize (c)) return false;
+    if (!u.format.sanitize (c)) return TRACE_RETURN (false);
     switch (u.format) {
-    case 1: return u.format1.sanitize (c);
-    default:return true;
+    case 1: return TRACE_RETURN (u.format1.sanitize (c));
+    default:return TRACE_RETURN (true);
     }
   }
 
@@ -552,8 +548,7 @@ struct Ligature
   public:
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    return ligGlyph.sanitize (c)
-        && component.sanitize (c);
+    return TRACE_RETURN (ligGlyph.sanitize (c) && component.sanitize (c));
   }
 
   private:
@@ -609,7 +604,7 @@ struct LigatureSet
   public:
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    return ligature.sanitize (c, this);
+    return TRACE_RETURN (ligature.sanitize (c, this));
   }
 
   private:
@@ -658,8 +653,7 @@ struct LigatureSubstFormat1
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    return coverage.sanitize (c, this)
-	&& ligatureSet.sanitize (c, this);
+    return TRACE_RETURN (coverage.sanitize (c, this) && ligatureSet.sanitize (c, this));
   }
 
   private:
@@ -708,10 +702,10 @@ struct LigatureSubst
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    if (!u.format.sanitize (c)) return false;
+    if (!u.format.sanitize (c)) return TRACE_RETURN (false);
     switch (u.format) {
-    case 1: return u.format1.sanitize (c);
-    default:return true;
+    case 1: return TRACE_RETURN (u.format1.sanitize (c));
+    default:return TRACE_RETURN (true);
     }
   }
 
@@ -852,14 +846,13 @@ struct ReverseChainSingleSubstFormat1
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    if (!(coverage.sanitize (c, this)
-       && backtrack.sanitize (c, this)))
-      return false;
+    if (!(coverage.sanitize (c, this) && backtrack.sanitize (c, this)))
+      return TRACE_RETURN (false);
     OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage> > (backtrack);
     if (!lookahead.sanitize (c, this))
-      return false;
+      return TRACE_RETURN (false);
     ArrayOf<GlyphID> &substitute = StructAfter<ArrayOf<GlyphID> > (lookahead);
-    return substitute.sanitize (c);
+    return TRACE_RETURN (substitute.sanitize (c));
   }
 
   private:
@@ -908,10 +901,10 @@ struct ReverseChainSingleSubst
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    if (!u.format.sanitize (c)) return false;
+    if (!u.format.sanitize (c)) return TRACE_RETURN (false);
     switch (u.format) {
-    case 1: return u.format1.sanitize (c);
-    default:return true;
+    case 1: return TRACE_RETURN (u.format1.sanitize (c));
+    default:return TRACE_RETURN (true);
     }
   }
 
@@ -968,7 +961,7 @@ struct SubstLookupSubTable
     case Multiple:		return u.multiple.would_apply (glyph_id);
     case Alternate:		return u.alternate.would_apply (glyph_id);
     case Extension:		return u.extension.would_apply (glyph_id);
-    default:return false;
+    default:			return false;
     }
   }
   inline bool would_apply (hb_codepoint_t first,
@@ -978,7 +971,7 @@ struct SubstLookupSubTable
     switch (lookup_type) {
     case Ligature:		return u.ligature.would_apply (first, second);
     case Extension:		return u.extension.would_apply (first, second);
-    default:return false;
+    default:			return false;
     }
   }
 
@@ -994,22 +987,22 @@ struct SubstLookupSubTable
     case ChainContext:		return u.chainContext.apply (c);
     case Extension:		return u.extension.apply (c);
     case ReverseChainSingle:	return u.reverseChainContextSingle.apply (c);
-    default:return false;
+    default:			return false;
     }
   }
 
   inline bool sanitize (hb_sanitize_context_t *c, unsigned int lookup_type) {
     TRACE_SANITIZE ();
     switch (lookup_type) {
-    case Single:		return u.single.sanitize (c);
-    case Multiple:		return u.multiple.sanitize (c);
-    case Alternate:		return u.alternate.sanitize (c);
-    case Ligature:		return u.ligature.sanitize (c);
-    case Context:		return u.c.sanitize (c);
-    case ChainContext:		return u.chainContext.sanitize (c);
-    case Extension:		return u.extension.sanitize (c);
-    case ReverseChainSingle:	return u.reverseChainContextSingle.sanitize (c);
-    default:return true;
+    case Single:		return TRACE_RETURN (u.single.sanitize (c));
+    case Multiple:		return TRACE_RETURN (u.multiple.sanitize (c));
+    case Alternate:		return TRACE_RETURN (u.alternate.sanitize (c));
+    case Ligature:		return TRACE_RETURN (u.ligature.sanitize (c));
+    case Context:		return TRACE_RETURN (u.c.sanitize (c));
+    case ChainContext:		return TRACE_RETURN (u.chainContext.sanitize (c));
+    case Extension:		return TRACE_RETURN (u.extension.sanitize (c));
+    case ReverseChainSingle:	return TRACE_RETURN (u.reverseChainContextSingle.sanitize (c));
+    default:			return TRACE_RETURN (true);
     }
   }
 
@@ -1147,9 +1140,9 @@ struct SubstLookup : Lookup
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    if (unlikely (!Lookup::sanitize (c))) return false;
+    if (unlikely (!Lookup::sanitize (c))) return TRACE_RETURN (false);
     OffsetArrayOf<SubstLookupSubTable> &list = CastR<OffsetArrayOf<SubstLookupSubTable> > (subTable);
-    return list.sanitize (c, this, get_type ());
+    return TRACE_RETURN (list.sanitize (c, this, get_type ()));
   }
 };
 
@@ -1178,9 +1171,9 @@ struct GSUB : GSUBGPOS
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    if (unlikely (!GSUBGPOS::sanitize (c))) return false;
+    if (unlikely (!GSUBGPOS::sanitize (c))) return TRACE_RETURN (false);
     OffsetTo<SubstLookupList> &list = CastR<OffsetTo<SubstLookupList> > (lookupList);
-    return list.sanitize (c, this);
+    return TRACE_RETURN (list.sanitize (c, this));
   }
   public:
   DEFINE_SIZE_STATIC (10);
@@ -1230,10 +1223,10 @@ inline bool ExtensionSubst::apply (hb_apply_context_t *c) const
 inline bool ExtensionSubst::sanitize (hb_sanitize_context_t *c)
 {
   TRACE_SANITIZE ();
-  if (unlikely (!Extension::sanitize (c))) return false;
+  if (unlikely (!Extension::sanitize (c))) return TRACE_RETURN (false);
   unsigned int offset = get_offset ();
-  if (unlikely (!offset)) return true;
-  return StructAtOffset<SubstLookupSubTable> (this, offset).sanitize (c, get_type ());
+  if (unlikely (!offset)) return TRACE_RETURN (true);
+  return TRACE_RETURN (StructAtOffset<SubstLookupSubTable> (this, offset).sanitize (c, get_type ()));
 }
 
 inline bool ExtensionSubst::is_reverse (void) const
diff --git a/src/hb-ot-layout-gsubgpos-private.hh b/src/hb-ot-layout-gsubgpos-private.hh
index 50e4437..136fff2 100644
--- a/src/hb-ot-layout-gsubgpos-private.hh
+++ b/src/hb-ot-layout-gsubgpos-private.hh
@@ -69,7 +69,7 @@ static inline uint8_t allocate_lig_id (hb_buffer_t *buffer) {
 #endif
 
 #define TRACE_CLOSURE() \
-	hb_auto_trace_t<HB_DEBUG_CLOSURE, unsigned int> trace (&c->debug_depth, "CLOSURE", this, HB_FUNC, "");
+	hb_auto_trace_t<HB_DEBUG_CLOSURE> trace (&c->debug_depth, "CLOSURE", this, HB_FUNC, "");
 
 
 
@@ -96,7 +96,7 @@ struct hb_closure_context_t
 #endif
 
 #define TRACE_APPLY() \
-	hb_auto_trace_t<HB_DEBUG_APPLY, unsigned int> trace (&c->debug_depth, "APPLY", this, HB_FUNC, "");
+	hb_auto_trace_t<HB_DEBUG_APPLY> trace (&c->debug_depth, "APPLY", this, HB_FUNC, "");
 
 
 
@@ -381,7 +381,7 @@ struct LookupRecord
 {
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    return c->check_struct (this);
+    return TRACE_RETURN (c->check_struct (this));
   }
 
   USHORT	sequenceIndex;		/* Index into current glyph
@@ -583,7 +583,7 @@ struct RuleSet
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    return rule.sanitize (c, this);
+    return TRACE_RETURN (rule.sanitize (c, this));
   }
 
   private:
@@ -637,8 +637,7 @@ struct ContextFormat1
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    return coverage.sanitize (c, this)
-	&& ruleSet.sanitize (c, this);
+    return TRACE_RETURN (coverage.sanitize (c, this) && ruleSet.sanitize (c, this));
   }
 
   private:
@@ -700,9 +699,7 @@ struct ContextFormat2
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    return coverage.sanitize (c, this)
-        && classDef.sanitize (c, this)
-	&& ruleSet.sanitize (c, this);
+    return TRACE_RETURN (coverage.sanitize (c, this) && classDef.sanitize (c, this) && ruleSet.sanitize (c, this));
   }
 
   private:
@@ -764,13 +761,13 @@ struct ContextFormat3
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    if (!c->check_struct (this)) return false;
+    if (!c->check_struct (this)) return TRACE_RETURN (false);
     unsigned int count = glyphCount;
-    if (!c->check_array (coverage, coverage[0].static_size, count)) return false;
+    if (!c->check_array (coverage, coverage[0].static_size, count)) return TRACE_RETURN (false);
     for (unsigned int i = 0; i < count; i++)
-      if (!coverage[i].sanitize (c, this)) return false;
+      if (!coverage[i].sanitize (c, this)) return TRACE_RETURN (false);
     LookupRecord *lookupRecord = &StructAtOffset<LookupRecord> (coverage, coverage[0].static_size * count);
-    return c->check_array (lookupRecord, lookupRecord[0].static_size, lookupCount);
+    return TRACE_RETURN (c->check_array (lookupRecord, lookupRecord[0].static_size, lookupCount));
   }
 
   private:
@@ -815,12 +812,12 @@ struct Context
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    if (!u.format.sanitize (c)) return false;
+    if (!u.format.sanitize (c)) return TRACE_RETURN (false);
     switch (u.format) {
-    case 1: return u.format1.sanitize (c);
-    case 2: return u.format2.sanitize (c);
-    case 3: return u.format3.sanitize (c);
-    default:return true;
+    case 1: return TRACE_RETURN (u.format1.sanitize (c));
+    case 2: return TRACE_RETURN (u.format2.sanitize (c));
+    case 3: return TRACE_RETURN (u.format3.sanitize (c));
+    default:return TRACE_RETURN (true);
     }
   }
 
@@ -945,13 +942,13 @@ struct ChainRule
   public:
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    if (!backtrack.sanitize (c)) return false;
+    if (!backtrack.sanitize (c)) return TRACE_RETURN (false);
     HeadlessArrayOf<USHORT> &input = StructAfter<HeadlessArrayOf<USHORT> > (backtrack);
-    if (!input.sanitize (c)) return false;
+    if (!input.sanitize (c)) return TRACE_RETURN (false);
     ArrayOf<USHORT> &lookahead = StructAfter<ArrayOf<USHORT> > (input);
-    if (!lookahead.sanitize (c)) return false;
+    if (!lookahead.sanitize (c)) return TRACE_RETURN (false);
     ArrayOf<LookupRecord> &lookup = StructAfter<ArrayOf<LookupRecord> > (lookahead);
-    return lookup.sanitize (c);
+    return TRACE_RETURN (lookup.sanitize (c));
   }
 
   private:
@@ -997,7 +994,7 @@ struct ChainRuleSet
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    return rule.sanitize (c, this);
+    return TRACE_RETURN (rule.sanitize (c, this));
   }
 
   private:
@@ -1049,8 +1046,7 @@ struct ChainContextFormat1
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    return coverage.sanitize (c, this)
-	&& ruleSet.sanitize (c, this);
+    return TRACE_RETURN (coverage.sanitize (c, this) && ruleSet.sanitize (c, this));
   }
 
   private:
@@ -1120,11 +1116,9 @@ struct ChainContextFormat2
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    return coverage.sanitize (c, this)
-	&& backtrackClassDef.sanitize (c, this)
-	&& inputClassDef.sanitize (c, this)
-	&& lookaheadClassDef.sanitize (c, this)
-	&& ruleSet.sanitize (c, this);
+    return TRACE_RETURN (coverage.sanitize (c, this) && backtrackClassDef.sanitize (c, this) &&
+			 inputClassDef.sanitize (c, this) && lookaheadClassDef.sanitize (c, this) &&
+			 ruleSet.sanitize (c, this));
   }
 
   private:
@@ -1204,13 +1198,13 @@ struct ChainContextFormat3
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    if (!backtrack.sanitize (c, this)) return false;
+    if (!backtrack.sanitize (c, this)) return TRACE_RETURN (false);
     OffsetArrayOf<Coverage> &input = StructAfter<OffsetArrayOf<Coverage> > (backtrack);
-    if (!input.sanitize (c, this)) return false;
+    if (!input.sanitize (c, this)) return TRACE_RETURN (false);
     OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage> > (input);
-    if (!lookahead.sanitize (c, this)) return false;
+    if (!lookahead.sanitize (c, this)) return TRACE_RETURN (false);
     ArrayOf<LookupRecord> &lookup = StructAfter<ArrayOf<LookupRecord> > (lookahead);
-    return lookup.sanitize (c);
+    return TRACE_RETURN (lookup.sanitize (c));
   }
 
   private:
@@ -1262,12 +1256,12 @@ struct ChainContext
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    if (!u.format.sanitize (c)) return false;
+    if (!u.format.sanitize (c)) return TRACE_RETURN (false);
     switch (u.format) {
-    case 1: return u.format1.sanitize (c);
-    case 2: return u.format2.sanitize (c);
-    case 3: return u.format3.sanitize (c);
-    default:return true;
+    case 1: return TRACE_RETURN (u.format1.sanitize (c));
+    case 2: return TRACE_RETURN (u.format2.sanitize (c));
+    case 3: return TRACE_RETURN (u.format3.sanitize (c));
+    default:return TRACE_RETURN (true);
     }
   }
 
@@ -1291,7 +1285,7 @@ struct ExtensionFormat1
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    return c->check_struct (this);
+    return TRACE_RETURN (c->check_struct (this));
   }
 
   private:
@@ -1324,10 +1318,10 @@ struct Extension
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    if (!u.format.sanitize (c)) return false;
+    if (!u.format.sanitize (c)) return TRACE_RETURN (false);
     switch (u.format) {
-    case 1: return u.format1.sanitize (c);
-    default:return true;
+    case 1: return TRACE_RETURN (u.format1.sanitize (c));
+    default:return TRACE_RETURN (true);
     }
   }
 
@@ -1381,10 +1375,10 @@ struct GSUBGPOS
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    return version.sanitize (c) && likely (version.major == 1)
-	&& scriptList.sanitize (c, this)
-	&& featureList.sanitize (c, this)
-	&& lookupList.sanitize (c, this);
+    return TRACE_RETURN (version.sanitize (c) && likely (version.major == 1) &&
+			 scriptList.sanitize (c, this) &&
+			 featureList.sanitize (c, this) &&
+			 lookupList.sanitize (c, this));
   }
 
   protected:
diff --git a/src/hb-ot-maxp-table.hh b/src/hb-ot-maxp-table.hh
index c3ac1c2..e270490 100644
--- a/src/hb-ot-maxp-table.hh
+++ b/src/hb-ot-maxp-table.hh
@@ -1,5 +1,5 @@
 /*
- * Copyright © 2011  Google, Inc.
+ * Copyright © 2011,2012  Google, Inc.
  *
  *  This is part of HarfBuzz, a text shaping library.
  *
@@ -47,9 +47,8 @@ struct maxp
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    return c->check_struct (this) &&
-	   likely (version.major == 1 ||
-		   (version.major == 0 && version.minor == 0x5000));
+    return TRACE_RETURN (c->check_struct (this) &&
+			 likely (version.major == 1 || (version.major == 0 && version.minor == 0x5000)));
   }
 
   /* We only implement version 0.5 as none of the extra fields in version 1.0 are useful. */
diff --git a/src/hb-ot-name-table.hh b/src/hb-ot-name-table.hh
index 0e9f7a4..9077c8c 100644
--- a/src/hb-ot-name-table.hh
+++ b/src/hb-ot-name-table.hh
@@ -1,5 +1,5 @@
 /*
- * Copyright © 2011  Google, Inc.
+ * Copyright © 2011,2012  Google, Inc.
  *
  *  This is part of HarfBuzz, a text shaping library.
  *
@@ -57,8 +57,7 @@ struct NameRecord
   inline bool sanitize (hb_sanitize_context_t *c, void *base) {
     TRACE_SANITIZE ();
     /* We can check from base all the way up to the end of string... */
-    return c->check_struct (this) &&
-	   c->check_range ((char *) base, (unsigned int) length + offset);
+    return TRACE_RETURN (c->check_struct (this) && c->check_range ((char *) base, (unsigned int) length + offset));
   }
 
   USHORT	platformID;	/* Platform ID. */
@@ -102,16 +101,16 @@ struct name
     char *string_pool = (char *) this + stringOffset;
     unsigned int _count = count;
     for (unsigned int i = 0; i < _count; i++)
-      if (!nameRecord[i].sanitize (c, string_pool)) return false;
-    return true;
+      if (!nameRecord[i].sanitize (c, string_pool)) return TRACE_RETURN (false);
+    return TRACE_RETURN (true);
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
-    return c->check_struct (this) &&
-	   likely (format == 0 || format == 1) &&
-	   c->check_array (nameRecord, nameRecord[0].static_size, count) &&
-	   sanitize_records (c);
+    return TRACE_RETURN (c->check_struct (this) &&
+			 likely (format == 0 || format == 1) &&
+			 c->check_array (nameRecord, nameRecord[0].static_size, count) &&
+			 sanitize_records (c));
   }
 
   /* We only implement format 0 for now. */
diff --git a/src/hb-private.hh b/src/hb-private.hh
index 535ea3a..cac1608 100644
--- a/src/hb-private.hh
+++ b/src/hb-private.hh
@@ -1,6 +1,6 @@
 /*
  * Copyright © 2007,2008,2009  Red Hat, Inc.
- * Copyright © 2011  Google, Inc.
+ * Copyright © 2011,2012  Google, Inc.
  *
  *  This is part of HarfBuzz, a text shaping library.
  *
@@ -519,7 +519,7 @@ _hb_debug_msg_va (const char *what,
   fprintf (stderr, "%-10s", what ? what : "");
 
   if (obj)
-    fprintf (stderr, "(%p) ", obj);
+    fprintf (stderr, "(%0*x) ", (unsigned int) (2 * sizeof (void *)), (unsigned long) obj);
   else
     fprintf (stderr, " %*s  ", (unsigned int) (2 * sizeof (void *)), "");
 
@@ -601,14 +601,14 @@ _hb_debug_msg<0> (const char *what,
  * Trace
  */
 
-template <int max_level, typename T>
+template <int max_level>
 struct hb_auto_trace_t {
   explicit inline hb_auto_trace_t (unsigned int *plevel_,
 				   const char *what,
 				   const void *obj,
 				   const char *func,
 				   const char *message,
-				   ...) : plevel(plevel_)
+				   ...) : plevel(plevel_), returned (false)
   {
     if (plevel) ++*plevel;
 
@@ -617,26 +617,49 @@ struct hb_auto_trace_t {
     _hb_debug_msg_va<max_level> (what, obj, func, TRUE, plevel ? *plevel : 0, +1, message, ap);
     va_end (ap);
   }
-  ~hb_auto_trace_t (void)
+  inline ~hb_auto_trace_t (void)
   {
-    _hb_debug_msg<max_level> (NULL, NULL, NULL, TRUE, plevel ? *plevel : 1, -1, " ");
+    if (unlikely (!returned)) {
+      fprintf (stderr, "OUCH, returned with no call to TRACE_RETURN.  This is a bug, please report.  Level was %d.\n", plevel ? *plevel : -1);
+      _hb_debug_msg<max_level> (NULL, NULL, NULL, TRUE, plevel ? *plevel : 1, -1, " ");
+      return;
+    }
 
     if (plevel) --*plevel;
   }
 
+  inline bool ret (bool v)
+  {
+    if (unlikely (returned)) {
+      fprintf (stderr, "OUCH, double calls to TRACE_RETURN.  This is a bug, please report.\n");
+      return v;
+    }
+
+    _hb_debug_msg<max_level> (NULL, NULL, NULL, TRUE, plevel ? *plevel : 1, -1, "return %s", v ? "true" : "false");
+    if (plevel) --*plevel;
+    plevel = NULL;
+    returned = true;
+    return v;
+  }
+
   private:
   unsigned int *plevel;
+  bool returned;
 };
-template <typename T> /* Optimize when tracing is disabled */
-struct hb_auto_trace_t<0, T> {
+template <> /* Optimize when tracing is disabled */
+struct hb_auto_trace_t<0> {
   explicit inline hb_auto_trace_t (unsigned int *plevel_,
 				   const char *what,
 				   const void *obj,
 				   const char *func,
 				   const char *message,
 				   ...) {}
+
+  template <typename T>
+  inline T ret (T v) { return v; }
 };
 
+#define TRACE_RETURN(RET) trace.ret (RET)
 
 /* Misc */
 
commit 829e814ff358c5e700ba4df54932696801aa9f65
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Fri May 11 00:52:16 2012 +0200

    Minor

diff --git a/src/hb-private.hh b/src/hb-private.hh
index a82773f..535ea3a 100644
--- a/src/hb-private.hh
+++ b/src/hb-private.hh
@@ -501,7 +501,7 @@ _hb_debug (unsigned int level,
 #define DEBUG_LEVEL(WHAT, LEVEL) (_hb_debug ((LEVEL), HB_DEBUG_##WHAT))
 #define DEBUG(WHAT) (DEBUG_LEVEL (WHAT, 0))
 
-template <int max_level> inline bool /* always returns TRUE */
+template <int max_level> inline void
 _hb_debug_msg_va (const char *what,
 		  const void *obj,
 		  const char *func,
@@ -512,7 +512,7 @@ _hb_debug_msg_va (const char *what,
 		  va_list ap)
 {
   if (!_hb_debug (level, max_level))
-    return TRUE;
+    return;
 
   static const char bars[] = "││││││││││││││││││││││││││││││││││││││││";
 
@@ -538,10 +538,8 @@ _hb_debug_msg_va (const char *what,
     vfprintf (stderr, message, ap);
 
   fprintf (stderr, "\n");
-
-  return TRUE;
 }
-template <> inline bool /* always returns TRUE */
+template <> inline void
 _hb_debug_msg_va<0> (const char *what,
 		     const void *obj,
 		     const char *func,
@@ -549,12 +547,9 @@ _hb_debug_msg_va<0> (const char *what,
 		     unsigned int level,
 		     int level_dir,
 		     const char *message,
-		     va_list ap)
-{
-  return TRUE;
-}
+		     va_list ap) {}
 
-template <int max_level> inline bool /* always returns TRUE */
+template <int max_level> inline void
 _hb_debug_msg (const char *what,
 	       const void *obj,
 	       const char *func,
@@ -563,7 +558,7 @@ _hb_debug_msg (const char *what,
 	       int level_dir,
 	       const char *message,
 	       ...) HB_PRINTF_FUNC(7, 8);
-template <int max_level> inline bool /* always returns TRUE */
+template <int max_level> inline void
 _hb_debug_msg (const char *what,
 	       const void *obj,
 	       const char *func,
@@ -575,14 +570,10 @@ _hb_debug_msg (const char *what,
 {
   va_list ap;
   va_start (ap, message);
-
-  bool ret = _hb_debug_msg_va<max_level> (what, obj, func, indented, level, level_dir, message, ap);
-
+  _hb_debug_msg_va<max_level> (what, obj, func, indented, level, level_dir, message, ap);
   va_end (ap);
-
-  return ret;
 }
-template <> inline bool /* always returns TRUE */
+template <> inline void
 _hb_debug_msg<0> (const char *what,
 		  const void *obj,
 		  const char *func,
@@ -591,7 +582,7 @@ _hb_debug_msg<0> (const char *what,
 		  int level_dir,
 		  const char *message,
 		  ...) HB_PRINTF_FUNC(7, 8);
-template <> inline bool /* always returns TRUE */
+template <> inline void
 _hb_debug_msg<0> (const char *what,
 		  const void *obj,
 		  const char *func,
@@ -599,10 +590,7 @@ _hb_debug_msg<0> (const char *what,
 		  unsigned int level,
 		  int level_dir,
 		  const char *message,
-		  ...)
-{
-  return TRUE;
-}
+		  ...) {}
 
 #define DEBUG_MSG_LEVEL(WHAT, OBJ, LEVEL, LEVEL_DIR, ...)	_hb_debug_msg<HB_DEBUG_##WHAT> (#WHAT, (OBJ), NULL,    TRUE, (LEVEL), (LEVEL_DIR), __VA_ARGS__)
 #define DEBUG_MSG(WHAT, OBJ, ...) 				_hb_debug_msg<HB_DEBUG_##WHAT> (#WHAT, (OBJ), NULL,    FALSE, 0, 0, __VA_ARGS__)
commit 6eec6f406d2cc13dbca422e88492d3d498af02bf
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Fri May 11 00:50:38 2012 +0200

    Code reshuffling

diff --git a/src/hb-private.hh b/src/hb-private.hh
index 5511e39..a82773f 100644
--- a/src/hb-private.hh
+++ b/src/hb-private.hh
@@ -511,19 +511,33 @@ _hb_debug_msg_va (const char *what,
 		  const char *message,
 		  va_list ap)
 {
+  if (!_hb_debug (level, max_level))
+    return TRUE;
+
   static const char bars[] = "││││││││││││││││││││││││││││││││││││││││";
 
-  (void) (_hb_debug (level, max_level) &&
-	  (fprintf (stderr, "%-10s", what ? what : ""), TRUE) &&
-	  ((obj && fprintf (stderr, "(%p) ", obj), TRUE) || fprintf (stderr, " %*s  ", (unsigned int) (2 * sizeof (void *)), ""), TRUE) &&
-	  (indented && fprintf (stderr, "%2d %s├%s",
-				level,
-				bars + sizeof (bars) - 1 - MIN ((unsigned int) sizeof (bars), 3 * level),
-				level_dir ? (level_dir > 0 ? "╮" : "╯") : "╴"), TRUE) &&
-	  (!indented && fprintf (stderr, "   ├╴"), TRUE) &&
-	  (func && fprintf (stderr, "%s: ", func), TRUE) &&
-	  (vfprintf (stderr, message, ap), TRUE) &&
-	  fprintf (stderr, "\n"));
+  fprintf (stderr, "%-10s", what ? what : "");
+
+  if (obj)
+    fprintf (stderr, "(%p) ", obj);
+  else
+    fprintf (stderr, " %*s  ", (unsigned int) (2 * sizeof (void *)), "");
+
+  if (indented)
+    fprintf (stderr, "%2d %s├%s",
+	     level,
+	     bars + sizeof (bars) - 1 - MIN ((unsigned int) sizeof (bars), 3 * level),
+	     level_dir ? (level_dir > 0 ? "╮" : "╯") : "╴");
+  else
+    fprintf (stderr, "   ├╴");
+
+  if (func)
+    fprintf (stderr, "%s: ", func);
+
+  if (message)
+    vfprintf (stderr, message, ap);
+
+  fprintf (stderr, "\n");
 
   return TRUE;
 }
commit 1e08830b4fac3a60ae52349cab6e101d389d30cd
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Fri May 11 00:16:40 2012 +0200

    Beautify debug output

diff --git a/src/hb-open-type-private.hh b/src/hb-open-type-private.hh
index 06b7300..25475c5 100644
--- a/src/hb-open-type-private.hh
+++ b/src/hb-open-type-private.hh
@@ -171,17 +171,17 @@ struct hb_sanitize_context_t
     this->edit_count = 0;
     this->debug_depth = 0;
 
-    DEBUG_MSG (SANITIZE, this->blob,
-	       "init [%p..%p] (%lu bytes)",
-	       this->start, this->end,
-	       (unsigned long) (this->end - this->start));
+    DEBUG_MSG_LEVEL (SANITIZE, this->blob, 0, +1,
+		     "init [%p..%p] (%lu bytes)",
+		     this->start, this->end,
+		     (unsigned long) (this->end - this->start));
   }
 
   inline void finish (void)
   {
-    DEBUG_MSG (SANITIZE, this->blob,
-	       "fini [%p..%p] %u edit requests",
-	       this->start, this->end, this->edit_count);
+    DEBUG_MSG_LEVEL (SANITIZE, this->blob, 0, -1,
+		     "fini [%p..%p] %u edit requests",
+		     this->start, this->end, this->edit_count);
 
     hb_blob_destroy (this->blob);
     this->blob = NULL;
@@ -195,7 +195,7 @@ struct hb_sanitize_context_t
 	       p <= this->end &&
 	       (unsigned int) (this->end - p) >= len;
 
-    DEBUG_MSG_LEVEL (SANITIZE, this->blob, this->debug_depth + 1,
+    DEBUG_MSG_LEVEL (SANITIZE, this->blob, this->debug_depth + 1, 0,
 		     "range [%p..%p] (%d bytes) in [%p..%p] -> %s",
 		     p, p + len, len,
 		     this->start, this->end,
@@ -209,7 +209,7 @@ struct hb_sanitize_context_t
     const char *p = (const char *) base;
     bool overflows = _hb_unsigned_int_mul_overflows (len, record_size);
 
-    DEBUG_MSG_LEVEL (SANITIZE, this->blob, this->debug_depth + 1,
+    DEBUG_MSG_LEVEL (SANITIZE, this->blob, this->debug_depth + 1, 0,
 		     "array [%p..%p] (%d*%d=%ld bytes) in [%p..%p] -> %s",
 		     p, p + (record_size * len), record_size, len, (unsigned long) record_size * len,
 		     this->start, this->end,
@@ -229,7 +229,7 @@ struct hb_sanitize_context_t
     const char *p = (const char *) base;
     this->edit_count++;
 
-    DEBUG_MSG_LEVEL (SANITIZE, this->blob, this->debug_depth + 1,
+    DEBUG_MSG_LEVEL (SANITIZE, this->blob, this->debug_depth + 1, 0,
 		     "edit(%u) [%p..%p] (%d bytes) in [%p..%p] -> %s",
 		     this->edit_count,
 		     p, p + len, len,
diff --git a/src/hb-private.hh b/src/hb-private.hh
index 07445c3..5511e39 100644
--- a/src/hb-private.hh
+++ b/src/hb-private.hh
@@ -503,18 +503,24 @@ _hb_debug (unsigned int level,
 
 template <int max_level> inline bool /* always returns TRUE */
 _hb_debug_msg_va (const char *what,
-	       const void *obj,
-	       const char *func,
-	       bool indented,
-	       int level,
-	       const char *message,
-	       va_list ap)
+		  const void *obj,
+		  const char *func,
+		  bool indented,
+		  unsigned int level,
+		  int level_dir,
+		  const char *message,
+		  va_list ap)
 {
+  static const char bars[] = "││││││││││││││││││││││││││││││││││││││││";
+
   (void) (_hb_debug (level, max_level) &&
-	  fprintf (stderr, "%s", what) &&
-	  (obj && fprintf (stderr, "(%p)", obj), TRUE) &&
-	  fprintf (stderr, ": ") &&
-	  (indented && fprintf (stderr, "%-*d-> ", level + 1, level), TRUE) &&
+	  (fprintf (stderr, "%-10s", what ? what : ""), TRUE) &&
+	  ((obj && fprintf (stderr, "(%p) ", obj), TRUE) || fprintf (stderr, " %*s  ", (unsigned int) (2 * sizeof (void *)), ""), TRUE) &&
+	  (indented && fprintf (stderr, "%2d %s├%s",
+				level,
+				bars + sizeof (bars) - 1 - MIN ((unsigned int) sizeof (bars), 3 * level),
+				level_dir ? (level_dir > 0 ? "╮" : "╯") : "╴"), TRUE) &&
+	  (!indented && fprintf (stderr, "   ├╴"), TRUE) &&
 	  (func && fprintf (stderr, "%s: ", func), TRUE) &&
 	  (vfprintf (stderr, message, ap), TRUE) &&
 	  fprintf (stderr, "\n"));
@@ -523,12 +529,13 @@ _hb_debug_msg_va (const char *what,
 }
 template <> inline bool /* always returns TRUE */
 _hb_debug_msg_va<0> (const char *what,
-		  const void *obj,
-		  const char *func,
-		  bool indented,
-		  int level,
-		  const char *message,
-		  va_list ap)
+		     const void *obj,
+		     const char *func,
+		     bool indented,
+		     unsigned int level,
+		     int level_dir,
+		     const char *message,
+		     va_list ap)
 {
   return TRUE;
 }
@@ -538,22 +545,24 @@ _hb_debug_msg (const char *what,
 	       const void *obj,
 	       const char *func,
 	       bool indented,
-	       int level,
+	       unsigned int level,
+	       int level_dir,
 	       const char *message,
-	       ...) HB_PRINTF_FUNC(6, 7);
+	       ...) HB_PRINTF_FUNC(7, 8);
 template <int max_level> inline bool /* always returns TRUE */
 _hb_debug_msg (const char *what,
 	       const void *obj,
 	       const char *func,
 	       bool indented,
-	       int level,
+	       unsigned int level,
+	       int level_dir,
 	       const char *message,
 	       ...)
 {
   va_list ap;
   va_start (ap, message);
 
-  bool ret = _hb_debug_msg_va<max_level> (what, obj, func, indented, level, message, ap);
+  bool ret = _hb_debug_msg_va<max_level> (what, obj, func, indented, level, level_dir, message, ap);
 
   va_end (ap);
 
@@ -564,24 +573,26 @@ _hb_debug_msg<0> (const char *what,
 		  const void *obj,
 		  const char *func,
 		  bool indented,
-		  int level,
+		  unsigned int level,
+		  int level_dir,
 		  const char *message,
-		  ...) HB_PRINTF_FUNC(6, 7);
+		  ...) HB_PRINTF_FUNC(7, 8);
 template <> inline bool /* always returns TRUE */
 _hb_debug_msg<0> (const char *what,
 		  const void *obj,
 		  const char *func,
 		  bool indented,
-		  int level,
+		  unsigned int level,
+		  int level_dir,
 		  const char *message,
 		  ...)
 {
   return TRUE;
 }
 
-#define DEBUG_MSG_LEVEL(WHAT, OBJ, LEVEL, ...) _hb_debug_msg<HB_DEBUG_##WHAT> (#WHAT, (OBJ), NULL, TRUE, (LEVEL), __VA_ARGS__)
-#define DEBUG_MSG(WHAT, OBJ, ...) DEBUG_MSG_LEVEL (WHAT, OBJ, 0, __VA_ARGS__)
-#define DEBUG_MSG_FUNC(WHAT, OBJ, ...) _hb_debug_msg<HB_DEBUG_##WHAT> (#WHAT, (OBJ), HB_FUNC, FALSE, 0, __VA_ARGS__)
+#define DEBUG_MSG_LEVEL(WHAT, OBJ, LEVEL, LEVEL_DIR, ...)	_hb_debug_msg<HB_DEBUG_##WHAT> (#WHAT, (OBJ), NULL,    TRUE, (LEVEL), (LEVEL_DIR), __VA_ARGS__)
+#define DEBUG_MSG(WHAT, OBJ, ...) 				_hb_debug_msg<HB_DEBUG_##WHAT> (#WHAT, (OBJ), NULL,    FALSE, 0, 0, __VA_ARGS__)
+#define DEBUG_MSG_FUNC(WHAT, OBJ, ...)				_hb_debug_msg<HB_DEBUG_##WHAT> (#WHAT, (OBJ), HB_FUNC, FALSE, 0, 0, __VA_ARGS__)
 
 
 /*
@@ -597,14 +608,19 @@ struct hb_auto_trace_t {
 				   const char *message,
 				   ...) : plevel(plevel_)
   {
-    if (max_level) ++*plevel;
+    if (plevel) ++*plevel;
 
     va_list ap;
     va_start (ap, message);
-    _hb_debug_msg_va<max_level> (what, obj, func, TRUE, *plevel, message, ap);
+    _hb_debug_msg_va<max_level> (what, obj, func, TRUE, plevel ? *plevel : 0, +1, message, ap);
     va_end (ap);
   }
-  ~hb_auto_trace_t (void) { if (max_level) --*plevel; }
+  ~hb_auto_trace_t (void)
+  {
+    _hb_debug_msg<max_level> (NULL, NULL, NULL, TRUE, plevel ? *plevel : 1, -1, " ");
+
+    if (plevel) --*plevel;
+  }
 
   private:
   unsigned int *plevel;
commit 6f4553801729a06e506ffdde7b27c72780d4bb80
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Thu May 10 23:24:43 2012 +0200

    More massaging trace messaging

diff --git a/src/hb-open-type-private.hh b/src/hb-open-type-private.hh
index bec78ee..06b7300 100644
--- a/src/hb-open-type-private.hh
+++ b/src/hb-open-type-private.hh
@@ -153,7 +153,7 @@ ASSERT_STATIC (Type::min_size + 1 <= sizeof (_Null##Type))
 
 
 #define TRACE_SANITIZE() \
-	hb_auto_trace_t<HB_DEBUG_SANITIZE, unsigned int> trace (&c->debug_depth, "SANITIZE", this, NULL, "%s", HB_FUNC);
+	hb_auto_trace_t<HB_DEBUG_SANITIZE, unsigned int> trace (&c->debug_depth, "SANITIZE", this, HB_FUNC, "");
 
 
 struct hb_sanitize_context_t
diff --git a/src/hb-ot-layout-gsubgpos-private.hh b/src/hb-ot-layout-gsubgpos-private.hh
index 79fe640..50e4437 100644
--- a/src/hb-ot-layout-gsubgpos-private.hh
+++ b/src/hb-ot-layout-gsubgpos-private.hh
@@ -69,7 +69,7 @@ static inline uint8_t allocate_lig_id (hb_buffer_t *buffer) {
 #endif
 
 #define TRACE_CLOSURE() \
-	hb_auto_trace_t<HB_DEBUG_CLOSURE, unsigned int> trace (&c->debug_depth, "CLOSURE", this, NULL, "%s", HB_FUNC);
+	hb_auto_trace_t<HB_DEBUG_CLOSURE, unsigned int> trace (&c->debug_depth, "CLOSURE", this, HB_FUNC, "");
 
 
 
@@ -96,7 +96,7 @@ struct hb_closure_context_t
 #endif
 
 #define TRACE_APPLY() \
-	hb_auto_trace_t<HB_DEBUG_APPLY, unsigned int> trace (&c->debug_depth, "APPLY", this, NULL, "%s", HB_FUNC);
+	hb_auto_trace_t<HB_DEBUG_APPLY, unsigned int> trace (&c->debug_depth, "APPLY", this, HB_FUNC, "");
 
 
 
diff --git a/src/hb-private.hh b/src/hb-private.hh
index e02d154..07445c3 100644
--- a/src/hb-private.hh
+++ b/src/hb-private.hh
@@ -514,9 +514,9 @@ _hb_debug_msg_va (const char *what,
 	  fprintf (stderr, "%s", what) &&
 	  (obj && fprintf (stderr, "(%p)", obj), TRUE) &&
 	  fprintf (stderr, ": ") &&
-	  (func && fprintf (stderr, "%s: ", func), TRUE) &&
 	  (indented && fprintf (stderr, "%-*d-> ", level + 1, level), TRUE) &&
-	  vfprintf (stderr, message, ap) &&
+	  (func && fprintf (stderr, "%s: ", func), TRUE) &&
+	  (vfprintf (stderr, message, ap), TRUE) &&
 	  fprintf (stderr, "\n"));
 
   return TRUE;
commit b5fa37cb694b01a7df3a656710391c40dd3fcc04
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Thu May 10 23:09:48 2012 +0200

    Minor

diff --git a/src/hb-set-private.hh b/src/hb-set-private.hh
index 7dd19b9..3a7eb81 100644
--- a/src/hb-set-private.hh
+++ b/src/hb-set-private.hh
@@ -133,7 +133,7 @@ struct _hb_set_t
   elt_t mask (hb_codepoint_t g) const { return elt_t (1) << (g & MASK); }
 
   hb_object_header_t header;
-  elt_t elts[ELTS]; /* 8kb */
+  elt_t elts[ELTS]; /* XXX 8kb */
 
   ASSERT_STATIC (sizeof (elt_t) * 8 == BITS);
   ASSERT_STATIC (sizeof (elt_t) * 8 * ELTS > MAX_G);
commit 208109703c929428c684ddcf9310b8ba780c4c31
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Thu May 10 23:06:58 2012 +0200

    Better trace message support infrastructure
    
    We have varargs in the trace interface now.  To be used soon...

diff --git a/src/hb-open-type-private.hh b/src/hb-open-type-private.hh
index 8143b28..bec78ee 100644
--- a/src/hb-open-type-private.hh
+++ b/src/hb-open-type-private.hh
@@ -153,7 +153,7 @@ ASSERT_STATIC (Type::min_size + 1 <= sizeof (_Null##Type))
 
 
 #define TRACE_SANITIZE() \
-	hb_auto_trace_t<HB_DEBUG_SANITIZE> trace (&c->debug_depth, "SANITIZE", this, NULL, HB_FUNC);
+	hb_auto_trace_t<HB_DEBUG_SANITIZE, unsigned int> trace (&c->debug_depth, "SANITIZE", this, NULL, "%s", HB_FUNC);
 
 
 struct hb_sanitize_context_t
@@ -195,9 +195,8 @@ struct hb_sanitize_context_t
 	       p <= this->end &&
 	       (unsigned int) (this->end - p) >= len;
 
-    DEBUG_MSG_LEVEL (SANITIZE, this->blob, this->debug_depth,
-		     "%-*d-> range [%p..%p] (%d bytes) in [%p..%p] -> %s",
-		     this->debug_depth, this->debug_depth,
+    DEBUG_MSG_LEVEL (SANITIZE, this->blob, this->debug_depth + 1,
+		     "range [%p..%p] (%d bytes) in [%p..%p] -> %s",
 		     p, p + len, len,
 		     this->start, this->end,
 		     ret ? "pass" : "FAIL");
@@ -210,9 +209,8 @@ struct hb_sanitize_context_t
     const char *p = (const char *) base;
     bool overflows = _hb_unsigned_int_mul_overflows (len, record_size);
 
-    DEBUG_MSG_LEVEL (SANITIZE, this->blob, this->debug_depth,
-		     "%-*d-> array [%p..%p] (%d*%d=%ld bytes) in [%p..%p] -> %s",
-		     this->debug_depth, this->debug_depth,
+    DEBUG_MSG_LEVEL (SANITIZE, this->blob, this->debug_depth + 1,
+		     "array [%p..%p] (%d*%d=%ld bytes) in [%p..%p] -> %s",
 		     p, p + (record_size * len), record_size, len, (unsigned long) record_size * len,
 		     this->start, this->end,
 		     !overflows ? "does not overflow" : "OVERFLOWS FAIL");
@@ -231,9 +229,8 @@ struct hb_sanitize_context_t
     const char *p = (const char *) base;
     this->edit_count++;
 
-    DEBUG_MSG_LEVEL (SANITIZE, this->blob, this->debug_depth,
-		     "%-*d-> edit(%u) [%p..%p] (%d bytes) in [%p..%p] -> %s",
-		     this->debug_depth, this->debug_depth,
+    DEBUG_MSG_LEVEL (SANITIZE, this->blob, this->debug_depth + 1,
+		     "edit(%u) [%p..%p] (%d bytes) in [%p..%p] -> %s",
 		     this->edit_count,
 		     p, p + len, len,
 		     this->start, this->end,
diff --git a/src/hb-ot-layout-gsubgpos-private.hh b/src/hb-ot-layout-gsubgpos-private.hh
index af02999..79fe640 100644
--- a/src/hb-ot-layout-gsubgpos-private.hh
+++ b/src/hb-ot-layout-gsubgpos-private.hh
@@ -69,7 +69,7 @@ static inline uint8_t allocate_lig_id (hb_buffer_t *buffer) {
 #endif
 
 #define TRACE_CLOSURE() \
-	hb_auto_trace_t<HB_DEBUG_CLOSURE> trace (&c->debug_depth, "CLOSURE", this, NULL, HB_FUNC);
+	hb_auto_trace_t<HB_DEBUG_CLOSURE, unsigned int> trace (&c->debug_depth, "CLOSURE", this, NULL, "%s", HB_FUNC);
 
 
 
@@ -96,7 +96,7 @@ struct hb_closure_context_t
 #endif
 
 #define TRACE_APPLY() \
-	hb_auto_trace_t<HB_DEBUG_APPLY> trace (&c->debug_depth, "APPLY", this, NULL, HB_FUNC);
+	hb_auto_trace_t<HB_DEBUG_APPLY, unsigned int> trace (&c->debug_depth, "APPLY", this, NULL, "%s", HB_FUNC);
 
 
 
diff --git a/src/hb-private.hh b/src/hb-private.hh
index 4f211a1..e02d154 100644
--- a/src/hb-private.hh
+++ b/src/hb-private.hh
@@ -502,6 +502,38 @@ _hb_debug (unsigned int level,
 #define DEBUG(WHAT) (DEBUG_LEVEL (WHAT, 0))
 
 template <int max_level> inline bool /* always returns TRUE */
+_hb_debug_msg_va (const char *what,
+	       const void *obj,
+	       const char *func,
+	       bool indented,
+	       int level,
+	       const char *message,
+	       va_list ap)
+{
+  (void) (_hb_debug (level, max_level) &&
+	  fprintf (stderr, "%s", what) &&
+	  (obj && fprintf (stderr, "(%p)", obj), TRUE) &&
+	  fprintf (stderr, ": ") &&
+	  (func && fprintf (stderr, "%s: ", func), TRUE) &&
+	  (indented && fprintf (stderr, "%-*d-> ", level + 1, level), TRUE) &&
+	  vfprintf (stderr, message, ap) &&
+	  fprintf (stderr, "\n"));
+
+  return TRUE;
+}
+template <> inline bool /* always returns TRUE */
+_hb_debug_msg_va<0> (const char *what,
+		  const void *obj,
+		  const char *func,
+		  bool indented,
+		  int level,
+		  const char *message,
+		  va_list ap)
+{
+  return TRUE;
+}
+
+template <int max_level> inline bool /* always returns TRUE */
 _hb_debug_msg (const char *what,
 	       const void *obj,
 	       const char *func,
@@ -521,18 +553,11 @@ _hb_debug_msg (const char *what,
   va_list ap;
   va_start (ap, message);
 
-  (void) (_hb_debug (level, max_level) &&
-	  fprintf (stderr, "%s", what) &&
-	  (obj && fprintf (stderr, "(%p)", obj), TRUE) &&
-	  fprintf (stderr, ": ") &&
-	  (func && fprintf (stderr, "%s: ", func), TRUE) &&
-	  (indented && fprintf (stderr, "%-*d-> ", level + 1, level), TRUE) &&
-	  vfprintf (stderr, message, ap) &&
-	  fprintf (stderr, "\n"));
+  bool ret = _hb_debug_msg_va<max_level> (what, obj, func, indented, level, message, ap);
 
   va_end (ap);
 
-  return TRUE;
+  return ret;
 }
 template <> inline bool /* always returns TRUE */
 _hb_debug_msg<0> (const char *what,
@@ -554,7 +579,7 @@ _hb_debug_msg<0> (const char *what,
   return TRUE;
 }
 
-#define DEBUG_MSG_LEVEL(WHAT, OBJ, LEVEL, ...) _hb_debug_msg<HB_DEBUG_##WHAT> (#WHAT, (OBJ), NULL, FALSE, (LEVEL), __VA_ARGS__)
+#define DEBUG_MSG_LEVEL(WHAT, OBJ, LEVEL, ...) _hb_debug_msg<HB_DEBUG_##WHAT> (#WHAT, (OBJ), NULL, TRUE, (LEVEL), __VA_ARGS__)
 #define DEBUG_MSG(WHAT, OBJ, ...) DEBUG_MSG_LEVEL (WHAT, OBJ, 0, __VA_ARGS__)
 #define DEBUG_MSG_FUNC(WHAT, OBJ, ...) _hb_debug_msg<HB_DEBUG_##WHAT> (#WHAT, (OBJ), HB_FUNC, FALSE, 0, __VA_ARGS__)
 
@@ -563,30 +588,35 @@ _hb_debug_msg<0> (const char *what,
  * Trace
  */
 
-template <int max_level>
+template <int max_level, typename T>
 struct hb_auto_trace_t {
   explicit inline hb_auto_trace_t (unsigned int *plevel_,
 				   const char *what,
 				   const void *obj,
 				   const char *func,
-				   const char *message) : plevel(plevel_)
+				   const char *message,
+				   ...) : plevel(plevel_)
   {
     if (max_level) ++*plevel;
-    /* TODO support variadic args here */
-    _hb_debug_msg<max_level> (what, obj, func, TRUE, *plevel, "%s", message);
+
+    va_list ap;
+    va_start (ap, message);
+    _hb_debug_msg_va<max_level> (what, obj, func, TRUE, *plevel, message, ap);
+    va_end (ap);
   }
   ~hb_auto_trace_t (void) { if (max_level) --*plevel; }
 
   private:
   unsigned int *plevel;
 };
-template <> /* Optimize when tracing is disabled */
-struct hb_auto_trace_t<0> {
+template <typename T> /* Optimize when tracing is disabled */
+struct hb_auto_trace_t<0, T> {
   explicit inline hb_auto_trace_t (unsigned int *plevel_,
 				   const char *what,
 				   const void *obj,
 				   const char *func,
-				   const char *message) {}
+				   const char *message,
+				   ...) {}
 };
 
 
commit 02b2922fbf098c8282eb23dc2c54d5829cf67024
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Thu May 10 21:44:50 2012 +0200

    [Indic] Towards better Reph positioning
    
    Fixed for Deva cases with two full-form consonants.  Failures **way** down.
    Not much left to go :-).

diff --git a/src/hb-ot-shape-complex-indic.cc b/src/hb-ot-shape-complex-indic.cc
index dce9125..4d82e24 100644
--- a/src/hb-ot-shape-complex-indic.cc
+++ b/src/hb-ot-shape-complex-indic.cc
@@ -541,10 +541,26 @@ final_reordering_syllable (hb_buffer_t *buffer,
       info[start].indic_position() == POS_RA_TO_BECOME_REPH &&
       info[start + 1].indic_position() != POS_RA_TO_BECOME_REPH)
   {
+      unsigned int new_reph_pos;
+
+     enum reph_position_t {
+       REPH_AFTER_MAIN, /* Malayalam, Oriya */
+       REPH_BEFORE_SUBSCRIPT, /* Gurmukhi  */
+       REPH_AFTER_SUBSCRIPT, /* Bengali */
+       REPH_BEFORE_POSTSCRIPT, /* Devanagari, Gujarati  */
+       REPH_AFTER_POSTSCRIPT, /* Kannada, Tamil, Telugu  */
+     } reph_pos = REPH_BEFORE_POSTSCRIPT; /* XXX */ /* XXX Figure out old behavior too */
+
     /*       1. If reph should be positioned after post-base consonant forms,
      *          proceed to step 5.
-     *
-     *       2. If the reph repositioning class is not after post-base: target
+     */
+    reph_step_1:
+    {
+      if (reph_pos == REPH_AFTER_POSTSCRIPT)
+	goto reph_step_5;
+    }
+
+    /*       2. If the reph repositioning class is not after post-base: target
      *          position is after the first explicit halant glyph between the
      *          first post-reph consonant and last main consonant. If ZWJ or ZWNJ
      *          are following this halant, position is moved after it. If such
@@ -554,49 +570,82 @@ final_reordering_syllable (hb_buffer_t *buffer,
      *          Note: in old-implementation fonts, where classifications were
      *          fixed in shaping engine, there was no case where reph position
      *          will be found on this step.
-     *
-     *       3. If reph should be repositioned after the main consonant: from the
+     */
+    reph_step_2:
+    {
+      new_reph_pos = start + 1;
+      while (new_reph_pos < base && info[new_reph_pos].indic_category() != OT_H)
+	new_reph_pos++;
+
+      if (new_reph_pos < base && info[new_reph_pos].indic_category() == OT_H) {
+	/* ->If ZWJ or ZWNJ are following this halant, position is moved after it. */
+	if (new_reph_pos + 1 < base && is_joiner (info[new_reph_pos + 1]))
+	  new_reph_pos++;
+	goto reph_move;
+      }
+    }
+
+    /*       3. If reph should be repositioned after the main consonant: find the
      *          first consonant not ligated with main, or find the first
      *          consonant that is not a potential pre-base reordering Ra.
-     *
-     *
-     *       4. If reph should be positioned before post-base consonant, find
+     */
+    reph_step_3:
+    {
+      /* XXX */
+    }
+
+    /*       4. If reph should be positioned before post-base consonant, find
      *          first post-base classified consonant not ligated with main. If no
      *          consonant is found, the target position should be before the
      *          first matra, syllable modifier sign or vedic sign.
-     *
-     *       5. If no consonant is found in steps 3 or 4, move reph to a position
+     */
+    reph_step_4:
+    {
+      /* XXX */
+    }
+
+    /*       5. If no consonant is found in steps 3 or 4, move reph to a position
      *          immediately before the first post-base matra, syllable modifier
      *          sign or vedic sign that has a reordering class after the intended
      *          reph position. For example, if the reordering position for reph
      *          is post-main, it will skip above-base matras that also have a
      *          post-main position.
-     *
-     *       6. Otherwise, reorder reph to the end of the syllable.
      */
+    reph_step_5:
+    {
+      /* XXX */
+    }
 
-    /* Now let's go shopping for a position. */
-    unsigned int new_reph_pos = end - 1;
-    while (new_reph_pos > start && (FLAG (info[new_reph_pos].indic_position()) & (FLAG (POS_SMVD))))
-      new_reph_pos--;
-
-    if (unlikely (info[new_reph_pos].indic_category() == OT_H)) {
-      /* *If* the Reph is to be ending up after a Matra,Halant sequence,
-       * position it before that Halant so it can interact with the Matra.
-       * However, if it's a plain Consonant,Halant we shouldn't do that.
-       */
-      for (unsigned int i = base + 1; i < new_reph_pos; i++)
-	if (info[i].indic_category() == OT_M) {
-	  /* Ok, got it. */
-	  new_reph_pos--;
-	}
+    /*       6. Otherwise, reorder reph to the end of the syllable.
+     */
+    reph_step_6:
+    {
+      new_reph_pos = end - 1;
+      while (new_reph_pos > start && info[new_reph_pos].indic_position() == POS_SMVD)
+	new_reph_pos--;
+
+      if (unlikely (info[new_reph_pos].indic_category() == OT_H)) {
+	/* *If* the Reph is to be ending up after a Matra,Halant sequence,
+	 * position it before that Halant so it can interact with the Matra.
+	 * However, if it's a plain Consonant,Halant we shouldn't do that.
+	 */
+	for (unsigned int i = base + 1; i < new_reph_pos; i++)
+	  if (info[i].indic_category() == OT_M) {
+	    /* Ok, got it. */
+	    new_reph_pos--;
+	  }
+      }
+      goto reph_move;
     }
 
-    /* Move */
-    hb_glyph_info_t reph = info[start];
-    memmove (&info[start], &info[start + 1], (new_reph_pos - start) * sizeof (info[0]));
-    info[new_reph_pos] = reph;
-    start_of_last_cluster = start; /* Yay, one big cluster! */
+    reph_move:
+    {
+      /* Move */
+      hb_glyph_info_t reph = info[start];
+      memmove (&info[start], &info[start + 1], (new_reph_pos - start) * sizeof (info[0]));
+      info[new_reph_pos] = reph;
+      start_of_last_cluster = start; /* Yay, one big cluster! */
+    }
   }
 
 
commit 74e54cf446bb979e488685e8c09eeed6b9d03c24
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Thu May 10 21:20:37 2012 +0200

    [Indic] Add Ra back for scripts without Reph
    
    We now check that the 'rphp' table exists before forming Reph, so
    we don't need to comment out Ra for those scripts.

diff --git a/src/hb-ot-shape-complex-indic-private.hh b/src/hb-ot-shape-complex-indic-private.hh
index b074916..d63b61a 100644
--- a/src/hb-ot-shape-complex-indic-private.hh
+++ b/src/hb-ot-shape-complex-indic-private.hh
@@ -252,19 +252,19 @@ static const struct consonant_position_t {
 
 /* XXX
  * This is a hack for now.  We should move this data into the main Indic table.
+ * Or completely remove it and just check in the tables.
  */
 static const hb_codepoint_t ra_chars[] = {
   0x0930, /* Devanagari */
   0x09B0, /* Bengali */
   0x09F0, /* Bengali */
-//0x09F1, /* Bengali */
-//0x0A30, /* Gurmukhi */
+  0x0A30, /* Gurmukhi */	/* No Reph */
   0x0AB0, /* Gujarati */
   0x0B30, /* Oriya */
-//0x0BB0, /* Tamil */
-//0x0C30, /* Telugu */
+  0x0BB0, /* Tamil */		/* No Reph */
+  0x0C30, /* Telugu */		/* No Reph */
   0x0CB0, /* Kannada */
-//0x0D30, /* Malayalam */
+  0x0D30, /* Malayalam */	/* No Reph */
 };
 
 
commit 2b70df5cc008617453b12bafeaac50e6d61b3224
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Thu May 10 18:38:22 2012 +0200

    [Indic] Add note re Uniscribe clusters

diff --git a/src/hb-ot-shape-complex-indic.cc b/src/hb-ot-shape-complex-indic.cc
index 1ce2541..dce9125 100644
--- a/src/hb-ot-shape-complex-indic.cc
+++ b/src/hb-ot-shape-complex-indic.cc
@@ -619,6 +619,10 @@ final_reordering_syllable (hb_buffer_t *buffer,
 
   /* Finish off the clusters and go home! */
 
+  /* For testing purposes we want to enable this to test against Uniscribe.
+   * One day when we don't compare to Uniscribe output anymore, we want to
+   * disable this because we believe it would make for superior cursor
+   * positioning. */
   if (1) {
     /* This is what Uniscribe does.  Ie. add cluster boundaries after Halant,ZWNJ.
      * This means, half forms are submerged into the main consonants cluster.
commit 21d2803133c2c424ed37a9f3d17c7fc4963e5a60
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Thu May 10 18:34:34 2012 +0200

    [Indic] Do clustering like Uniscribe does
    
    Hindi Wikipedia failures down to 6639 (0.938381%)!

diff --git a/src/hb-ot-shape-complex-indic.cc b/src/hb-ot-shape-complex-indic.cc
index b0e8ade..1ce2541 100644
--- a/src/hb-ot-shape-complex-indic.cc
+++ b/src/hb-ot-shape-complex-indic.cc
@@ -575,8 +575,6 @@ final_reordering_syllable (hb_buffer_t *buffer,
      *       6. Otherwise, reorder reph to the end of the syllable.
      */
 
-    start_of_last_cluster = start; /* Yay, one big cluster! */
-
     /* Now let's go shopping for a position. */
     unsigned int new_reph_pos = end - 1;
     while (new_reph_pos > start && (FLAG (info[new_reph_pos].indic_position()) & (FLAG (POS_SMVD))))
@@ -598,6 +596,7 @@ final_reordering_syllable (hb_buffer_t *buffer,
     hb_glyph_info_t reph = info[start];
     memmove (&info[start], &info[start + 1], (new_reph_pos - start) * sizeof (info[0]));
     info[new_reph_pos] = reph;
+    start_of_last_cluster = start; /* Yay, one big cluster! */
   }
 
 
@@ -617,8 +616,25 @@ final_reordering_syllable (hb_buffer_t *buffer,
    *          consonant.
    */
 
-  /* TODO */
-  buffer->merge_clusters (start, end);
+
+  /* Finish off the clusters and go home! */
+
+  if (1) {
+    /* This is what Uniscribe does.  Ie. add cluster boundaries after Halant,ZWNJ.
+     * This means, half forms are submerged into the main consonants cluster.
+     * This is unnecessary, and makes cursor positioning harder, but that's what
+     * Uniscribe does. */
+    unsigned int cluster_start = start;
+    for (unsigned int i = start + 1; i < start_of_last_cluster; i++)
+      if (info[i - 1].indic_category() == OT_H && info[i].indic_category() == OT_ZWNJ) {
+        i++;
+	buffer->merge_clusters (cluster_start, i);
+	cluster_start = i;
+      }
+    start_of_last_cluster = cluster_start;
+  }
+
+  buffer->merge_clusters (start_of_last_cluster, end);
 }
 
 
commit b20c9ebaf5176101fdfcffbe4714a2e619dd94b6
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Thu May 10 18:31:17 2012 +0200

    [Indic] Add test for matra group
    
    The spec says: "[{M}+[N]+[H]]", and that's what Uniscribe implements.
    We instead do: "{M+[N]+[H]}", which means we allow Nukta and Halant
    after all Matras, not just the last one.  It makes more sense.

diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-devanagari/misc/MANIFEST b/test/shaping/texts/in-tree/shaper-indic/indic/script-devanagari/misc/MANIFEST
index f7ed29c..d361ae0 100644
--- a/test/shaping/texts/in-tree/shaper-indic/indic/script-devanagari/misc/MANIFEST
+++ b/test/shaping/texts/in-tree/shaper-indic/indic/script-devanagari/misc/MANIFEST
@@ -1,3 +1,4 @@
 joiners.txt
 misc.txt
+spec-deviations.txt
 tricky-reordering.txt
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-devanagari/misc/spec-deviations.txt b/test/shaping/texts/in-tree/shaper-indic/indic/script-devanagari/misc/spec-deviations.txt
new file mode 100644
index 0000000..4814019
--- /dev/null
+++ b/test/shaping/texts/in-tree/shaper-indic/indic/script-devanagari/misc/spec-deviations.txt
@@ -0,0 +1 @@
+सा़े
commit 8df5636968389ac7bf8620ccd091fd4872b0bbee
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Thu May 10 15:41:04 2012 +0200

    [Indic] Reorder Reph to before the Halant after Matras
    
    Uniscribe doesn't do it, but we want to do as it gives the Reph the
    opportunity to interact with the Matras.  Test with mangal for example.
    Sequence: <0930,094d,0915,094b,094d>
    In test suite already.

diff --git a/src/hb-ot-shape-complex-indic.cc b/src/hb-ot-shape-complex-indic.cc
index 575880b..b0e8ade 100644
--- a/src/hb-ot-shape-complex-indic.cc
+++ b/src/hb-ot-shape-complex-indic.cc
@@ -582,6 +582,18 @@ final_reordering_syllable (hb_buffer_t *buffer,
     while (new_reph_pos > start && (FLAG (info[new_reph_pos].indic_position()) & (FLAG (POS_SMVD))))
       new_reph_pos--;
 
+    if (unlikely (info[new_reph_pos].indic_category() == OT_H)) {
+      /* *If* the Reph is to be ending up after a Matra,Halant sequence,
+       * position it before that Halant so it can interact with the Matra.
+       * However, if it's a plain Consonant,Halant we shouldn't do that.
+       */
+      for (unsigned int i = base + 1; i < new_reph_pos; i++)
+	if (info[i].indic_category() == OT_M) {
+	  /* Ok, got it. */
+	  new_reph_pos--;
+	}
+    }
+
     /* Move */
     hb_glyph_info_t reph = info[start];
     memmove (&info[start], &info[start + 1], (new_reph_pos - start) * sizeof (info[0]));
commit daf3234bdc82c669302599a76d2b14f5e69989db
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Thu May 10 15:28:27 2012 +0200

    [Indic] Don't clear the mask for Reph
    
    This was removing the mandatory global 1 bit in the mask and hence
    disabling GPOS for Reph!

diff --git a/src/hb-ot-shape-complex-indic.cc b/src/hb-ot-shape-complex-indic.cc
index 6790cc2..575880b 100644
--- a/src/hb-ot-shape-complex-indic.cc
+++ b/src/hb-ot-shape-complex-indic.cc
@@ -382,7 +382,7 @@ initial_reordering_consonant_syllable (const hb_ot_map_t *map, hb_buffer_t *buff
 
     /* Reph */
     if (has_reph)
-      info[start].mask = mask_array[RPHF];
+      info[start].mask |= mask_array[RPHF];
 
     /* Pre-base */
     mask = mask_array[HALF] | mask_array[AKHN] | mask_array[CJCT];
commit 7708ee23cbcc8c8edce13e73b6e549b77bd8c2d0
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Thu May 10 14:48:25 2012 +0200

    [Indic] Improve Left Matra repositioning
    
    Move its dependents too.

diff --git a/src/hb-ot-shape-complex-indic.cc b/src/hb-ot-shape-complex-indic.cc
index 05857eb..6790cc2 100644
--- a/src/hb-ot-shape-complex-indic.cc
+++ b/src/hb-ot-shape-complex-indic.cc
@@ -513,7 +513,7 @@ final_reordering_syllable (hb_buffer_t *buffer,
 
     /* Now go see if there's actually any matras... */
     for (unsigned int i = new_matra_pos; i > start; i--)
-      if (info[i - 1].indic_category () == OT_M)
+      if (info[i - 1].indic_position () == POS_LEFT_MATRA)
       {
 	unsigned int old_matra_pos = i - 1;
 	hb_glyph_info_t matra = info[old_matra_pos];
commit 61a58e26a5bda16851669404fc8206896e124740
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Thu May 10 14:43:53 2012 +0200

    [Indic] Add tricky reordering test cases
    
    In the case of Consonant,LeftMatra,Halant, Uniscribe leaves the Halant
    where it is, but we want to move it with the Matra as that makes more
    logical sense.

diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-devanagari/misc/MANIFEST b/test/shaping/texts/in-tree/shaper-indic/indic/script-devanagari/misc/MANIFEST
index 4ee921e..f7ed29c 100644
--- a/test/shaping/texts/in-tree/shaper-indic/indic/script-devanagari/misc/MANIFEST
+++ b/test/shaping/texts/in-tree/shaper-indic/indic/script-devanagari/misc/MANIFEST
@@ -1,2 +1,3 @@
 joiners.txt
 misc.txt
+tricky-reordering.txt
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-devanagari/misc/tricky-reordering.txt b/test/shaping/texts/in-tree/shaper-indic/indic/script-devanagari/misc/tricky-reordering.txt
new file mode 100644
index 0000000..6c6bcea
--- /dev/null
+++ b/test/shaping/texts/in-tree/shaper-indic/indic/script-devanagari/misc/tricky-reordering.txt
@@ -0,0 +1,4 @@
+Usage:
+  ./hb-unicode-encode UNICODE_STRING...
+or:
+  ./hb-unicode-encode --stdin
commit dbb105883c6e9b83e78dc8b10766cd56b98cd7e1
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Thu May 10 13:45:52 2012 +0200

    [Indic] Do Reph repositioning in final reordering like the spec says
    
    This introduced a failure, which we tracked down to a test case like this:
    
      U+092E,U+094B,U+094D,U+0930
    
    The final character is a Ra that should be put in a syllable of it's
    own.  And we do.  But it will interact with the Halant before it.  So
    now we finally are convinced that we have to limit features to syllable
    boundaries.  That's coming after lunch!

diff --git a/src/hb-ot-shape-complex-indic-private.hh b/src/hb-ot-shape-complex-indic-private.hh
index 8f5144e..b074916 100644
--- a/src/hb-ot-shape-complex-indic-private.hh
+++ b/src/hb-ot-shape-complex-indic-private.hh
@@ -74,8 +74,6 @@ enum indic_position_t {
 
   POS_MATRAS,
 
-  POS_REPH,
-
   POS_SMVD
 };
 
diff --git a/src/hb-ot-shape-complex-indic.cc b/src/hb-ot-shape-complex-indic.cc
index 1b32ddb..05857eb 100644
--- a/src/hb-ot-shape-complex-indic.cc
+++ b/src/hb-ot-shape-complex-indic.cc
@@ -332,10 +332,8 @@ initial_reordering_consonant_syllable (const hb_ot_map_t *map, hb_buffer_t *buff
   info[base].indic_position() = POS_BASE_C;
 
   /* Handle beginning Ra */
-  if (has_reph) {
-    info[start].indic_position() = POS_REPH;
-    info[start].mask = mask_array[RPHF];
-  }
+  if (has_reph)
+    info[start].indic_position() = POS_RA_TO_BECOME_REPH;
 
   /* For old-style Indic script tags, move the first post-base Halant after
    * last consonant. */
@@ -382,6 +380,10 @@ initial_reordering_consonant_syllable (const hb_ot_map_t *map, hb_buffer_t *buff
   {
     hb_mask_t mask;
 
+    /* Reph */
+    if (has_reph)
+      info[start].mask = mask_array[RPHF];
+
     /* Pre-base */
     mask = mask_array[HALF] | mask_array[AKHN] | mask_array[CJCT];
     for (i = start; i < base; i++)
@@ -530,41 +532,64 @@ final_reordering_syllable (hb_buffer_t *buffer,
    *     it will be reordered according to the basic-forms shaping results.
    *     Possible positions for reph, depending on the script, are; after main,
    *     before post-base consonant forms, and after post-base consonant forms.
-   *
-   *       1. If reph should be positioned after post-base consonant forms,
-   *          proceed to step 5.
-   *
-   *       2. If the reph repositioning class is not after post-base: target
-   *          position is after the first explicit halant glyph between the
-   *          first post-reph consonant and last main consonant. If ZWJ or ZWNJ
-   *          are following this halant, position is moved after it. If such
-   *          position is found, this is the target position. Otherwise,
-   *          proceed to the next step.
-   *
-   *          Note: in old-implementation fonts, where classifications were
-   *          fixed in shaping engine, there was no case where reph position
-   *          will be found on this step.
-   *
-   *       3. If reph should be repositioned after the main consonant: from the
-   *          first consonant not ligated with main, or find the first
-   *          consonant that is not a potential pre-base reordering Ra.
-   *
-   *
-   *       4. If reph should be positioned before post-base consonant, find
-   *          first post-base classified consonant not ligated with main. If no
-   *          consonant is found, the target position should be before the
-   *          first matra, syllable modifier sign or vedic sign.
-   *
-   *       5. If no consonant is found in steps 3 or 4, move reph to a position
-   *          immediately before the first post-base matra, syllable modifier
-   *          sign or vedic sign that has a reordering class after the intended
-   *          reph position. For example, if the reordering position for reph
-   *          is post-main, it will skip above-base matras that also have a
-   *          post-main position.
-   *
-   *       6. Otherwise, reorder reph to the end of the syllable.
-   *
-   *   o Reorder pre-base reordering consonants:
+   */
+
+  /* If there's anything after the Ra that has the REPH pos, it ought to be halant.
+   * Which means that the font has failed to ligate the Reph.  In which case, we
+   * shouldn't move. */
+  if (start + 1 < end &&
+      info[start].indic_position() == POS_RA_TO_BECOME_REPH &&
+      info[start + 1].indic_position() != POS_RA_TO_BECOME_REPH)
+  {
+    /*       1. If reph should be positioned after post-base consonant forms,
+     *          proceed to step 5.
+     *
+     *       2. If the reph repositioning class is not after post-base: target
+     *          position is after the first explicit halant glyph between the
+     *          first post-reph consonant and last main consonant. If ZWJ or ZWNJ
+     *          are following this halant, position is moved after it. If such
+     *          position is found, this is the target position. Otherwise,
+     *          proceed to the next step.
+     *
+     *          Note: in old-implementation fonts, where classifications were
+     *          fixed in shaping engine, there was no case where reph position
+     *          will be found on this step.
+     *
+     *       3. If reph should be repositioned after the main consonant: from the
+     *          first consonant not ligated with main, or find the first
+     *          consonant that is not a potential pre-base reordering Ra.
+     *
+     *
+     *       4. If reph should be positioned before post-base consonant, find
+     *          first post-base classified consonant not ligated with main. If no
+     *          consonant is found, the target position should be before the
+     *          first matra, syllable modifier sign or vedic sign.
+     *
+     *       5. If no consonant is found in steps 3 or 4, move reph to a position
+     *          immediately before the first post-base matra, syllable modifier
+     *          sign or vedic sign that has a reordering class after the intended
+     *          reph position. For example, if the reordering position for reph
+     *          is post-main, it will skip above-base matras that also have a
+     *          post-main position.
+     *
+     *       6. Otherwise, reorder reph to the end of the syllable.
+     */
+
+    start_of_last_cluster = start; /* Yay, one big cluster! */
+
+    /* Now let's go shopping for a position. */
+    unsigned int new_reph_pos = end - 1;
+    while (new_reph_pos > start && (FLAG (info[new_reph_pos].indic_position()) & (FLAG (POS_SMVD))))
+      new_reph_pos--;
+
+    /* Move */
+    hb_glyph_info_t reph = info[start];
+    memmove (&info[start], &info[start + 1], (new_reph_pos - start) * sizeof (info[0]));
+    info[new_reph_pos] = reph;
+  }
+
+
+  /*   o Reorder pre-base reordering consonants:
    *
    *     If a pre-base reordering consonant is found, reorder it according to
    *     the following rules:
commit 4705a7026900e51f6430f03a73c87f2df035df92
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Thu May 10 13:09:08 2012 +0200

    Minor

diff --git a/src/hb-ot-shape-complex-indic.cc b/src/hb-ot-shape-complex-indic.cc
index ad8b967..1b32ddb 100644
--- a/src/hb-ot-shape-complex-indic.cc
+++ b/src/hb-ot-shape-complex-indic.cc
@@ -487,6 +487,8 @@ final_reordering_syllable (hb_buffer_t *buffer,
     return;
   }
 
+  unsigned int start_of_last_cluster = base;
+
   /*   o Reorder matras:
    *
    *     If a pre-base matra character had been reordered before applying basic
@@ -515,6 +517,7 @@ final_reordering_syllable (hb_buffer_t *buffer,
 	hb_glyph_info_t matra = info[old_matra_pos];
 	memmove (&info[old_matra_pos], &info[old_matra_pos + 1], (new_matra_pos - old_matra_pos) * sizeof (info[0]));
 	info[new_matra_pos] = matra;
+	start_of_last_cluster = MIN (new_matra_pos, start_of_last_cluster);
 	new_matra_pos--;
       }
   }
commit 4ac9e98d9d2ea973dd612dc4063cf78496c643a0
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Thu May 10 12:53:53 2012 +0200

    [Indic] Reorder left matras to be closer to base

diff --git a/src/hb-ot-shape-complex-indic-private.hh b/src/hb-ot-shape-complex-indic-private.hh
index 681391e..8f5144e 100644
--- a/src/hb-ot-shape-complex-indic-private.hh
+++ b/src/hb-ot-shape-complex-indic-private.hh
@@ -67,10 +67,6 @@ enum indic_position_t {
   POS_LEFT_MATRA,
 
   POS_PRE_C,
-
-  POS_BEFORE_HALFS,
-  POS_HALFS,
-
   POS_BASE_C,
   POS_ABOVE_C,
   POS_BELOW_C,
diff --git a/src/hb-ot-shape-complex-indic.cc b/src/hb-ot-shape-complex-indic.cc
index 67628ae..ad8b967 100644
--- a/src/hb-ot-shape-complex-indic.cc
+++ b/src/hb-ot-shape-complex-indic.cc
@@ -462,14 +462,32 @@ static void
 final_reordering_syllable (hb_buffer_t *buffer,
 			   unsigned int start, unsigned int end)
 {
+  hb_glyph_info_t *info = buffer->info;
+
   /* 4. Final reordering:
    *
    * After the localized forms and basic shaping forms GSUB features have been
    * applied (see below), the shaping engine performs some final glyph
    * reordering before applying all the remaining font features to the entire
    * cluster.
-   *
-   *   o Reorder matras:
+   */
+
+  /* Find base again */
+  unsigned int base = end;
+  for (unsigned int i = start; i < end; i++)
+    if (info[i].indic_position() == POS_BASE_C) {
+      base = i;
+      break;
+    }
+
+  if (base == start) {
+    /* There's no Reph, and no left Matra to reposition.  Just merge the cluster
+     * and go home. */
+    buffer->merge_clusters (start, end);
+    return;
+  }
+
+  /*   o Reorder matras:
    *
    *     If a pre-base matra character had been reordered before applying basic
    *     features, the glyph can be moved closer to the main consonant based on
@@ -477,8 +495,32 @@ final_reordering_syllable (hb_buffer_t *buffer,
    *     defined as “after last standalone halant glyph, after initial matra
    *     position and before the main consonant”. If ZWJ or ZWNJ follow this
    *     halant, position is moved after it.
-   *
-   *   o Reorder reph:
+   */
+
+  unsigned int new_matra_pos = base - 1;
+  while (new_matra_pos > start &&
+	 !(FLAG (info[new_matra_pos].indic_category()) & (FLAG (OT_M) | FLAG (OT_H))))
+    new_matra_pos--;
+  /* If we found no Halant we are done.  Otherwise... */
+  if (info[new_matra_pos].indic_category() == OT_H) {
+    /* -> If ZWJ or ZWNJ follow this halant, position is moved after it. */
+    if (new_matra_pos + 1 < end && is_joiner (info[new_matra_pos + 1]))
+      new_matra_pos++;
+
+    /* Now go see if there's actually any matras... */
+    for (unsigned int i = new_matra_pos; i > start; i--)
+      if (info[i - 1].indic_category () == OT_M)
+      {
+	unsigned int old_matra_pos = i - 1;
+	hb_glyph_info_t matra = info[old_matra_pos];
+	memmove (&info[old_matra_pos], &info[old_matra_pos + 1], (new_matra_pos - old_matra_pos) * sizeof (info[0]));
+	info[new_matra_pos] = matra;
+	new_matra_pos--;
+      }
+  }
+
+
+  /*   o Reorder reph:
    *
    *     Reph’s original position is always at the beginning of the syllable,
    *     (i.e. it is not reordered at the character reordering stage). However,
commit 1a1fa8c655a082fc1439608457ba717306cc83ca
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Thu May 10 12:20:21 2012 +0200

    [Indic] Treat the standalone cluster case reusing the consonant logic

diff --git a/src/hb-ot-shape-complex-indic.cc b/src/hb-ot-shape-complex-indic.cc
index a97c8af..67628ae 100644
--- a/src/hb-ot-shape-complex-indic.cc
+++ b/src/hb-ot-shape-complex-indic.cc
@@ -67,10 +67,12 @@ is_joiner (const hb_glyph_info_t &info)
 static bool
 is_consonant (const hb_glyph_info_t &info)
 {
-  /* Note: We treat Vowels as if they were consonants.  This is safe because Vowels
+  /* Note:
+   *
+   * We treat Vowels and NBSP as if they were consonants.  This is safe because Vowels
    * cannot happen in a consonant syllable.  The plus side however is, we can call the
    * consonant syllable logic from the vowel syllable function and get it all right! */
-  return !!(FLAG (info.indic_category()) & (FLAG (OT_C) | FLAG (OT_Ra) | FLAG (OT_V)));
+  return !!(FLAG (info.indic_category()) & (FLAG (OT_C) | FLAG (OT_Ra) | FLAG (OT_V) | FLAG (OT_NBSP)));
 }
 
 static const struct {
@@ -428,10 +430,8 @@ static void
 initial_reordering_standalone_cluster (const hb_ot_map_t *map, hb_buffer_t *buffer, hb_mask_t *mask_array,
 				       unsigned int start, unsigned int end)
 {
-  /* TODO
-   * Easiest thing to do here is to convert the NBSP to consonant and
-   * call initial_reordering_consonant_syllable.
-   */
+  /* We made the vowels look like consonants.  So let's call the consonant logic! */
+  initial_reordering_consonant_syllable (map, buffer, mask_array, start, end);
 }
 
 static void
commit 190eb31a16178269aecaf5d2ecc9012f956749f4
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Thu May 10 12:17:16 2012 +0200

    [Indic] Minor

diff --git a/src/hb-ot-shape-complex-indic.cc b/src/hb-ot-shape-complex-indic.cc
index c1d731c..a97c8af 100644
--- a/src/hb-ot-shape-complex-indic.cc
+++ b/src/hb-ot-shape-complex-indic.cc
@@ -343,7 +343,7 @@ initial_reordering_consonant_syllable (const hb_ot_map_t *map, hb_buffer_t *buff
       if (info[i].indic_category() == OT_H) {
         unsigned int j;
         for (j = end - 1; j > i; j--)
-	  if ((FLAG (info[j].indic_category()) & (FLAG (OT_C) | FLAG (OT_Ra))))
+	  if (is_consonant (info[j]))
 	    break;
 	if (j > i) {
 	  /* Move Halant to after last consonant. */
commit c5306b6861cfaa50af40e8ceb058791fa06d7981
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Thu May 10 12:07:33 2012 +0200

    [Indic] Handle Vowel syllables
    
    Reusing the consonant logic!

diff --git a/src/hb-ot-shape-complex-indic-private.hh b/src/hb-ot-shape-complex-indic-private.hh
index 7bf282e..681391e 100644
--- a/src/hb-ot-shape-complex-indic-private.hh
+++ b/src/hb-ot-shape-complex-indic-private.hh
@@ -83,8 +83,7 @@ enum indic_position_t {
   POS_SMVD
 };
 
-/* Categories used in IndicSyllabicCategory.txt from UCD */
-/* The assignments are guesswork */
+/* Categories used in IndicSyllabicCategory.txt from UCD. */
 enum indic_syllabic_category_t {
   INDIC_SYLLABIC_CATEGORY_OTHER			= OT_X,
 
diff --git a/src/hb-ot-shape-complex-indic.cc b/src/hb-ot-shape-complex-indic.cc
index dff464b..c1d731c 100644
--- a/src/hb-ot-shape-complex-indic.cc
+++ b/src/hb-ot-shape-complex-indic.cc
@@ -67,7 +67,10 @@ is_joiner (const hb_glyph_info_t &info)
 static bool
 is_consonant (const hb_glyph_info_t &info)
 {
-  return !!(FLAG (info.indic_category()) & (FLAG (OT_C) | FLAG (OT_Ra)));
+  /* Note: We treat Vowels as if they were consonants.  This is safe because Vowels
+   * cannot happen in a consonant syllable.  The plus side however is, we can call the
+   * consonant syllable logic from the vowel syllable function and get it all right! */
+  return !!(FLAG (info.indic_category()) & (FLAG (OT_C) | FLAG (OT_Ra) | FLAG (OT_V)));
 }
 
 static const struct {
@@ -417,10 +420,8 @@ static void
 initial_reordering_vowel_syllable (const hb_ot_map_t *map, hb_buffer_t *buffer, hb_mask_t *mask_array,
 				   unsigned int start, unsigned int end)
 {
-  /* TODO
-   * Not clear to me how this should work.  Do the matras move to before the
-   * independent vowel?  No idea.
-   */
+  /* We made the vowels look like consonants.  So let's call the consonant logic! */
+  initial_reordering_consonant_syllable (map, buffer, mask_array, start, end);
 }
 
 static void
commit 6d8e0cb74c02f6bc09cd4abe9e4bc82062e1b517
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Thu May 10 11:41:51 2012 +0200

    [Indic] Simplify Reph logic

diff --git a/src/hb-ot-shape-complex-indic.cc b/src/hb-ot-shape-complex-indic.cc
index 1f02101..dff464b 100644
--- a/src/hb-ot-shape-complex-indic.cc
+++ b/src/hb-ot-shape-complex-indic.cc
@@ -237,9 +237,10 @@ initial_reordering_consonant_syllable (const hb_ot_map_t *map, hb_buffer_t *buff
    *    base consonants. */
   unsigned int limit = start;
   if (mask_array[RPHF] &&
-      start + 2 < end &&
+      start + 3 <= end &&
       info[start].indic_category() == OT_Ra &&
-      info[start + 1].indic_category() == OT_H)
+      info[start + 1].indic_category() == OT_H &&
+      !is_joiner (info[start + 2]))
   {
     limit += 2;
     base = start;
@@ -267,15 +268,6 @@ initial_reordering_consonant_syllable (const hb_ot_map_t *map, hb_buffer_t *buff
        * TODO
        */
 
-      /* ->  o If the syllable starts with Ra + Halant (in a script that has Reph)
-       *       and has more than one consonant, Ra is excluded from candidates for
-       *       base consonants.
-       *
-       * IMPLEMENTATION NOTES:
-       *
-       * We do this by adjusting limit accordingly before entering the loop.
-       */
-
       /* -> or arrive at the first consonant. The consonant stopped at will
        * be the base. */
       base = i;
@@ -296,8 +288,6 @@ initial_reordering_consonant_syllable (const hb_ot_map_t *map, hb_buffer_t *buff
   }
 
 
-
-
   /* 2. Decompose and reorder Matras:
    *
    * Each matra and any syllable modifier sign in the cluster are moved to the
@@ -336,15 +326,11 @@ initial_reordering_consonant_syllable (const hb_ot_map_t *map, hb_buffer_t *buff
     info[i].indic_position() = POS_PRE_C;
   info[base].indic_position() = POS_BASE_C;
 
-
   /* Handle beginning Ra */
-  if (has_reph &&
-      start + 3 <= end &&
-      !is_joiner (info[start + 2]))
-   {
+  if (has_reph) {
     info[start].indic_position() = POS_REPH;
     info[start].mask = mask_array[RPHF];
-   }
+  }
 
   /* For old-style Indic script tags, move the first post-base Halant after
    * last consonant. */
commit 3d25079f8d6be81b9b4b91d3a97016b8a572f571
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Thu May 10 11:37:42 2012 +0200

    [Indic] Don't form Reph is Ra is the only consonant in the syllable

diff --git a/src/hb-ot-shape-complex-indic-private.hh b/src/hb-ot-shape-complex-indic-private.hh
index d65d3ac..7bf282e 100644
--- a/src/hb-ot-shape-complex-indic-private.hh
+++ b/src/hb-ot-shape-complex-indic-private.hh
@@ -63,7 +63,7 @@ enum indic_category_t {
 
 /* Visual positions in a syllable from left to right. */
 enum indic_position_t {
-  POS_RA,
+  POS_RA_TO_BECOME_REPH,
   POS_LEFT_MATRA,
 
   POS_PRE_C,
diff --git a/src/hb-ot-shape-complex-indic.cc b/src/hb-ot-shape-complex-indic.cc
index 65cff5f..1f02101 100644
--- a/src/hb-ot-shape-complex-indic.cc
+++ b/src/hb-ot-shape-complex-indic.cc
@@ -287,6 +287,16 @@ initial_reordering_consonant_syllable (const hb_ot_map_t *map, hb_buffer_t *buff
   if (base < start)
     base = start; /* Just in case... */
 
+  /* -> If the syllable starts with Ra + Halant (in a script that has Reph)
+   *    and has more than one consonant, Ra is excluded from candidates for
+   *    base consonants. */
+  if (has_reph && base == start) {
+    /* Have no other consonant, so Reph is not formed and Ra becomes base. */
+    has_reph = false;
+  }
+
+
+
 
   /* 2. Decompose and reorder Matras:
    *
commit b99d63ae114fb58f129562b293a8a66543d499ad
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Thu May 10 11:32:52 2012 +0200

    [Indic] Increase max syllable length
    
    20 was way too low, one could hit a syllable with 7ish consonants with it.

diff --git a/src/hb-ot-shape-complex-indic.cc b/src/hb-ot-shape-complex-indic.cc
index 466e924..65cff5f 100644
--- a/src/hb-ot-shape-complex-indic.cc
+++ b/src/hb-ot-shape-complex-indic.cc
@@ -363,7 +363,7 @@ initial_reordering_consonant_syllable (const hb_ot_map_t *map, hb_buffer_t *buff
       info[i].indic_position() = info[i - 1].indic_position();
 
   /* We do bubble-sort, skip malicious clusters attempts */
-  if (end - start < 20)
+  if (end - start < 64)
   {
     /* Sit tight, rock 'n roll! */
     hb_bubble_sort (info + start, end - start, compare_indic_order);
commit a391ff50b9a7b6ac3e58d199ea726b20ee6839bb
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Thu May 10 11:31:20 2012 +0200

    [Indic] Adjust base after sorting

diff --git a/src/hb-ot-shape-complex-indic.cc b/src/hb-ot-shape-complex-indic.cc
index 174e3a9..466e924 100644
--- a/src/hb-ot-shape-complex-indic.cc
+++ b/src/hb-ot-shape-complex-indic.cc
@@ -363,9 +363,18 @@ initial_reordering_consonant_syllable (const hb_ot_map_t *map, hb_buffer_t *buff
       info[i].indic_position() = info[i - 1].indic_position();
 
   /* We do bubble-sort, skip malicious clusters attempts */
-  /* Sit tight, rock 'n roll! */
   if (end - start < 20)
+  {
+    /* Sit tight, rock 'n roll! */
     hb_bubble_sort (info + start, end - start, compare_indic_order);
+    /* Find base again */
+    base = end;
+    for (i = start; i < end; i++)
+      if (info[i].indic_position() == POS_BASE_C) {
+        base = i;
+	break;
+      }
+  }
 
   /* Setup masks now */
 
commit d3637edb248162970e202e9d0671540274192844
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Thu May 10 10:51:38 2012 +0200

    [Indic] Don't return for long syllables.  Just not sort.

diff --git a/src/hb-ot-shape-complex-indic.cc b/src/hb-ot-shape-complex-indic.cc
index 9868b5e..174e3a9 100644
--- a/src/hb-ot-shape-complex-indic.cc
+++ b/src/hb-ot-shape-complex-indic.cc
@@ -363,11 +363,9 @@ initial_reordering_consonant_syllable (const hb_ot_map_t *map, hb_buffer_t *buff
       info[i].indic_position() = info[i - 1].indic_position();
 
   /* We do bubble-sort, skip malicious clusters attempts */
-  if (end - start > 20)
-    return;
-
   /* Sit tight, rock 'n roll! */
-  hb_bubble_sort (info + start, end - start, compare_indic_order);
+  if (end - start < 20)
+    hb_bubble_sort (info + start, end - start, compare_indic_order);
 
   /* Setup masks now */
 
commit dfa0cade7fce3791e47eaa7edcd23da76c7a0ed0
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Wed May 9 19:10:07 2012 +0200

    Fix Uniscribe clusters with multiple items

diff --git a/src/hb-uniscribe.cc b/src/hb-uniscribe.cc
index 41ce5e6..584d641 100644
--- a/src/hb-uniscribe.cc
+++ b/src/hb-uniscribe.cc
@@ -359,6 +359,9 @@ retry:
 				glyph_props + glyphs_offset,
 				(int *) &glyphs_len);
 
+      for (unsigned int j = chars_offset; j < chars_offset + item_chars_len; j++)
+        log_clusters[j] += glyphs_offset;
+
       if (unlikely (items[i].a.fNoGlyphIndex))
 	FAIL ("ScriptShapeOpenType() set fNoGlyphIndex");
       if (unlikely (hr == E_OUTOFMEMORY))
commit 86e5dd386a7989701da476db89be268e4ac1e219
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Wed May 9 18:57:37 2012 +0200

    [Indic] Don't give up syllable parsing upon junk

diff --git a/src/hb-ot-shape-complex-indic-machine.rl b/src/hb-ot-shape-complex-indic-machine.rl
index 907188a..2ec2798 100644
--- a/src/hb-ot-shape-complex-indic-machine.rl
+++ b/src/hb-ot-shape-complex-indic-machine.rl
@@ -74,13 +74,13 @@ action next_syllable {
 consonant_syllable =	(c.N? (H.z?|z.H))* c.N? A? (H.z? | matra_group*)? syllable_tail %(found_consonant_syllable);
 vowel_syllable =	(Ra H)? V N? (z?.H.c | ZWJ.c)? matra_group* syllable_tail %(found_vowel_syllable);
 standalone_cluster =	(Ra H)? NBSP N? (z? H c)? matra_group* syllable_tail %(found_standalone_cluster);
-non_indic = X %(found_non_indic);
+other = /./ %(found_non_indic);
 
 syllable =
 	  consonant_syllable
 	| vowel_syllable
 	| standalone_cluster
-	| non_indic
+	| other
 	;
 
 main := (syllable %(next_syllable))**;
commit ef24cc8c8e2478a6352c340f4611a617646de4cc
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Wed May 9 17:56:03 2012 +0200

    [Indic] Towards multi-cluster syllables and final reordering

diff --git a/src/hb-ot-shape-complex-arabic.cc b/src/hb-ot-shape-complex-arabic.cc
index 746f2d7..ac7457e 100644
--- a/src/hb-ot-shape-complex-arabic.cc
+++ b/src/hb-ot-shape-complex-arabic.cc
@@ -30,7 +30,7 @@
 
 
 /* buffer var allocations */
-#define arabic_shaping_action() complex_var_temporary_u16() /* arabic shaping action */
+#define arabic_shaping_action() complex_var_temporary_u8() /* arabic shaping action */
 
 
 /*
diff --git a/src/hb-ot-shape-complex-indic-machine.rl b/src/hb-ot-shape-complex-indic-machine.rl
index 6406c24..907188a 100644
--- a/src/hb-ot-shape-complex-indic-machine.rl
+++ b/src/hb-ot-shape-complex-indic-machine.rl
@@ -59,12 +59,17 @@ z = ZWJ|ZWNJ;
 matra_group = M N? H?;
 syllable_tail = SM? (VD VD?)?;
 
-action found_consonant_syllable { found_consonant_syllable (map, buffer, mask_array, last, p); }
-action found_vowel_syllable { found_vowel_syllable (map, buffer, mask_array, last, p); }
-action found_standalone_cluster { found_standalone_cluster (map, buffer, mask_array, last, p); }
-action found_non_indic { found_non_indic (map, buffer, mask_array, last, p); }
-
-action next_syllable { buffer->merge_clusters (last, p); last = p; }
+action found_consonant_syllable { initial_reordering_consonant_syllable (map, buffer, mask_array, last, p); }
+action found_vowel_syllable { initial_reordering_vowel_syllable (map, buffer, mask_array, last, p); }
+action found_standalone_cluster { initial_reordering_standalone_cluster (map, buffer, mask_array, last, p); }
+action found_non_indic { initial_reordering_non_indic (map, buffer, mask_array, last, p); }
+
+action next_syllable {
+  for (unsigned int i = last; i < p; i++)
+    info[i].indic_syllable() = syllable_serial;
+  last = p;
+  syllable_serial++;
+}
 
 consonant_syllable =	(c.N? (H.z?|z.H))* c.N? A? (H.z? | matra_group*)? syllable_tail %(found_consonant_syllable);
 vowel_syllable =	(Ra H)? V N? (z?.H.c | ZWJ.c)? matra_group* syllable_tail %(found_vowel_syllable);
@@ -88,15 +93,17 @@ find_syllables (const hb_ot_map_t *map, hb_buffer_t *buffer, hb_mask_t *mask_arr
 {
   unsigned int p, pe, eof;
   int cs;
+  hb_glyph_info_t *info = buffer->info;
   %%{
     write init;
-    getkey buffer->info[p].indic_category();
+    getkey info[p].indic_category();
   }%%
 
   p = 0;
   pe = eof = buffer->len;
 
   unsigned int last = 0;
+  uint8_t syllable_serial = 0;
   %%{
     write exec;
   }%%
diff --git a/src/hb-ot-shape-complex-indic-private.hh b/src/hb-ot-shape-complex-indic-private.hh
index babb1fc..d65d3ac 100644
--- a/src/hb-ot-shape-complex-indic-private.hh
+++ b/src/hb-ot-shape-complex-indic-private.hh
@@ -36,6 +36,7 @@
 /* buffer var allocations */
 #define indic_category() complex_var_persistent_u8_0() /* indic_category_t */
 #define indic_position() complex_var_persistent_u8_1() /* indic_matra_category_t */
+#define indic_syllable() complex_var_persistent_u8_2() /* serial */
 
 #define INDIC_TABLE_ELEMENT_TYPE uint8_t
 
diff --git a/src/hb-ot-shape-complex-indic.cc b/src/hb-ot-shape-complex-indic.cc
index 86ed307..9868b5e 100644
--- a/src/hb-ot-shape-complex-indic.cc
+++ b/src/hb-ot-shape-complex-indic.cc
@@ -163,6 +163,7 @@ _hb_ot_shape_complex_setup_masks_indic (hb_ot_map_t *map, hb_buffer_t *buffer, h
 {
   HB_BUFFER_ALLOCATE_VAR (buffer, indic_category);
   HB_BUFFER_ALLOCATE_VAR (buffer, indic_position);
+  HB_BUFFER_ALLOCATE_VAR (buffer, indic_syllable);
 
   /* We cannot setup masks here.  We save information about characters
    * and setup masks later on in a pause-callback. */
@@ -205,8 +206,8 @@ compare_indic_order (const hb_glyph_info_t *pa, const hb_glyph_info_t *pb)
 }
 
 static void
-found_consonant_syllable (const hb_ot_map_t *map, hb_buffer_t *buffer, hb_mask_t *mask_array,
-			  unsigned int start, unsigned int end)
+initial_reordering_consonant_syllable (const hb_ot_map_t *map, hb_buffer_t *buffer, hb_mask_t *mask_array,
+				       unsigned int start, unsigned int end)
 {
   unsigned int i;
   hb_glyph_info_t *info = buffer->info;
@@ -410,8 +411,8 @@ found_consonant_syllable (const hb_ot_map_t *map, hb_buffer_t *buffer, hb_mask_t
 
 
 static void
-found_vowel_syllable (const hb_ot_map_t *map, hb_buffer_t *buffer, hb_mask_t *mask_array,
-		      unsigned int start, unsigned int end)
+initial_reordering_vowel_syllable (const hb_ot_map_t *map, hb_buffer_t *buffer, hb_mask_t *mask_array,
+				   unsigned int start, unsigned int end)
 {
   /* TODO
    * Not clear to me how this should work.  Do the matras move to before the
@@ -420,17 +421,17 @@ found_vowel_syllable (const hb_ot_map_t *map, hb_buffer_t *buffer, hb_mask_t *ma
 }
 
 static void
-found_standalone_cluster (const hb_ot_map_t *map, hb_buffer_t *buffer, hb_mask_t *mask_array,
-			  unsigned int start, unsigned int end)
+initial_reordering_standalone_cluster (const hb_ot_map_t *map, hb_buffer_t *buffer, hb_mask_t *mask_array,
+				       unsigned int start, unsigned int end)
 {
   /* TODO
    * Easiest thing to do here is to convert the NBSP to consonant and
-   * call found_consonant_syllable.
+   * call initial_reordering_consonant_syllable.
    */
 }
 
 static void
-found_non_indic (const hb_ot_map_t *map, hb_buffer_t *buffer, hb_mask_t *mask_array,
+initial_reordering_non_indic (const hb_ot_map_t *map, hb_buffer_t *buffer, hb_mask_t *mask_array,
 		 unsigned int start, unsigned int end)
 {
   /* Nothing to do right now.  If we ever switch to using the output
@@ -454,10 +455,8 @@ initial_reordering (const hb_ot_map_t *map,
 }
 
 static void
-final_reordering (const hb_ot_map_t *map,
-		  hb_face_t *face,
-		  hb_buffer_t *buffer,
-		  void *user_data HB_UNUSED)
+final_reordering_syllable (hb_buffer_t *buffer,
+			   unsigned int start, unsigned int end)
 {
   /* 4. Final reordering:
    *
@@ -533,9 +532,31 @@ final_reordering (const hb_ot_map_t *map,
    */
 
   /* TODO */
+  buffer->merge_clusters (start, end);
+}
 
 
+static void
+final_reordering (const hb_ot_map_t *map,
+		  hb_face_t *face,
+		  hb_buffer_t *buffer,
+		  void *user_data HB_UNUSED)
+{
+  unsigned int count = buffer->len;
+  if (!count) return;
+
+  hb_glyph_info_t *info = buffer->info;
+  unsigned int last = 0;
+  unsigned int last_syllable = info[0].indic_syllable();
+  for (unsigned int i = 1; i < count; i++)
+    if (last_syllable != info[i].indic_syllable()) {
+      final_reordering_syllable (buffer, last, i);
+      last = i;
+      last_syllable = info[last].indic_syllable();
+    }
+  final_reordering_syllable (buffer, last, count);
 
+  HB_BUFFER_DEALLOCATE_VAR (buffer, indic_syllable);
   HB_BUFFER_DEALLOCATE_VAR (buffer, indic_category);
   HB_BUFFER_DEALLOCATE_VAR (buffer, indic_position);
 }
diff --git a/src/hb-ot-shape-complex-private.hh b/src/hb-ot-shape-complex-private.hh
index 3f99781..a4bc2f8 100644
--- a/src/hb-ot-shape-complex-private.hh
+++ b/src/hb-ot-shape-complex-private.hh
@@ -41,10 +41,9 @@
 /* buffer var allocations, used by complex shapers */
 #define complex_var_persistent_u8_0()	var2.u8[0]
 #define complex_var_persistent_u8_1()	var2.u8[1]
+#define complex_var_persistent_u8_2()	var2.u8[2]
 #define complex_var_persistent_u16()	var2.u16[0]
-#define complex_var_temporary_u8_0()	var2.u8[2]
-#define complex_var_temporary_u8_1()	var2.u8[3]
-#define complex_var_temporary_u16()	var2.u16[1]
+#define complex_var_temporary_u8()	var2.u8[3]
 
 
 #define HB_COMPLEX_SHAPERS_IMPLEMENT_SHAPERS \
commit a9844d41c6cb30d8a2d733130a0e72f51b6c81c1
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Wed May 9 17:53:13 2012 +0200

    Combine lig_id and lig_comp into one byte, to free up one for Indic

diff --git a/src/hb-ot-layout-gpos-table.hh b/src/hb-ot-layout-gpos-table.hh
index c51e89a..1b08505 100644
--- a/src/hb-ot-layout-gpos-table.hh
+++ b/src/hb-ot-layout-gpos-table.hh
@@ -1105,9 +1105,11 @@ struct MarkLigPosFormat1
      * is identical to the ligature ID of the found ligature.  If yes, we
      * can directly use the component index.  If not, we attach the mark
      * glyph to the last component of the ligature. */
-    if (c->buffer->info[j].lig_id() && c->buffer->info[j].lig_id() == c->buffer->info[c->buffer->idx].lig_id() && c->buffer->info[c->buffer->idx].lig_comp())
+    if (get_lig_id (c->buffer->info[j]) &&
+	get_lig_id (c->buffer->info[j]) == get_lig_id (c->buffer->info[c->buffer->idx]) &&
+	get_lig_comp (c->buffer->info[c->buffer->idx]) > 0)
     {
-      comp_index = c->buffer->info[c->buffer->idx].lig_comp() - 1;
+      comp_index = get_lig_comp (c->buffer->info[c->buffer->idx]) - 1;
       if (comp_index >= comp_count)
 	comp_index = comp_count - 1;
     }
@@ -1208,8 +1210,9 @@ struct MarkMarkPosFormat1
     /* Two marks match only if they belong to the same base, or same component
      * of the same ligature.  That is, the component numbers must match, and
      * if those are non-zero, the ligid number should also match. */
-    if ((c->buffer->info[j].lig_comp() != c->buffer->info[c->buffer->idx].lig_comp()) ||
-	(c->buffer->info[j].lig_comp() && c->buffer->info[j].lig_id() != c->buffer->info[c->buffer->idx].lig_id()))
+    if ((get_lig_comp (c->buffer->info[j]) != get_lig_comp (c->buffer->info[c->buffer->idx])) ||
+	(get_lig_comp (c->buffer->info[j]) > 0 &&
+	 get_lig_id (c->buffer->info[j]) != get_lig_id (c->buffer->info[c->buffer->idx])))
       return false;
 
     unsigned int mark2_index = (this+mark2Coverage) (c->buffer->info[j].codepoint);
@@ -1545,8 +1548,7 @@ GPOS::position_finish (hb_buffer_t *buffer)
   for (unsigned int i = 0; i < len; i++)
     fix_mark_attachment (pos, i, direction);
 
-  HB_BUFFER_DEALLOCATE_VAR (buffer, lig_comp);
-  HB_BUFFER_DEALLOCATE_VAR (buffer, lig_id);
+  HB_BUFFER_DEALLOCATE_VAR (buffer, lig_props);
   HB_BUFFER_DEALLOCATE_VAR (buffer, props_cache);
 }
 
diff --git a/src/hb-ot-layout-gsub-table.hh b/src/hb-ot-layout-gsub-table.hh
index 371672a..4c5fbaa 100644
--- a/src/hb-ot-layout-gsub-table.hh
+++ b/src/hb-ot-layout-gsub-table.hh
@@ -516,8 +516,7 @@ struct Ligature
 
     /* Allocate new ligature id */
     unsigned int lig_id = allocate_lig_id (c->buffer);
-    c->buffer->info[c->buffer->idx].lig_comp() = 0;
-    c->buffer->info[c->buffer->idx].lig_id() = lig_id;
+    set_lig_props (c->buffer->info[c->buffer->idx], lig_id, 0);
 
     if (skippy_iter.idx < c->buffer->idx + count) /* No input glyphs skipped */
     {
@@ -538,8 +537,7 @@ struct Ligature
       {
 	while (c->should_mark_skip_current_glyph ())
 	{
-	  c->buffer->info[c->buffer->idx].lig_comp() = i;
-	  c->buffer->info[c->buffer->idx].lig_id() = lig_id;
+	  set_lig_props (c->buffer->info[c->buffer->idx],  lig_id, i);
 	  c->replace_glyph (c->buffer->info[c->buffer->idx].codepoint);
 	}
 
@@ -1193,12 +1191,11 @@ void
 GSUB::substitute_start (hb_buffer_t *buffer)
 {
   HB_BUFFER_ALLOCATE_VAR (buffer, props_cache);
-  HB_BUFFER_ALLOCATE_VAR (buffer, lig_id);
-  HB_BUFFER_ALLOCATE_VAR (buffer, lig_comp);
+  HB_BUFFER_ALLOCATE_VAR (buffer, lig_props);
 
   unsigned int count = buffer->len;
   for (unsigned int i = 0; i < count; i++)
-    buffer->info[i].props_cache() = buffer->info[i].lig_id() = buffer->info[i].lig_comp() = 0;
+    buffer->info[i].props_cache() = buffer->info[i].lig_props() = 0;
 }
 
 void
diff --git a/src/hb-ot-layout-gsubgpos-private.hh b/src/hb-ot-layout-gsubgpos-private.hh
index 1d12db1..af02999 100644
--- a/src/hb-ot-layout-gsubgpos-private.hh
+++ b/src/hb-ot-layout-gsubgpos-private.hh
@@ -35,12 +35,30 @@
 
 
 /* buffer var allocations */
-#define lig_id() var2.u8[2] /* unique ligature id */
-#define lig_comp() var2.u8[3] /* component number in the ligature (0 = base) */
+#define lig_props() var2.u8[3]
+
+/* unique ligature id */
+/* component number in the ligature (0 = base) */
+static inline void
+set_lig_props (hb_glyph_info_t &info, unsigned int lig_id, unsigned int lig_comp)
+{
+  info.lig_props() = (lig_id << 4) | (lig_comp & 0x0F);
+}
+static inline unsigned int
+get_lig_id (hb_glyph_info_t &info)
+{
+  return info.lig_props() >> 4;
+}
+static inline unsigned int
+get_lig_comp (hb_glyph_info_t &info)
+{
+  return info.lig_props() & 0x0F;
+}
 
 static inline uint8_t allocate_lig_id (hb_buffer_t *buffer) {
-  uint8_t lig_id = buffer->next_serial ();
-  if (unlikely (!lig_id)) lig_id = buffer->next_serial (); /* in case of overflow */
+  uint8_t lig_id = buffer->next_serial () & 0x0F;
+  if (unlikely (!lig_id))
+    lig_id = allocate_lig_id (buffer); /* in case of overflow */
   return lig_id;
 }
 
commit 92332e5116271a5d96e532005fe750e7552a6cbb
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Wed May 9 17:40:00 2012 +0200

    Minor

diff --git a/src/hb-ot-shape-complex-indic.cc b/src/hb-ot-shape-complex-indic.cc
index b964861..86ed307 100644
--- a/src/hb-ot-shape-complex-indic.cc
+++ b/src/hb-ot-shape-complex-indic.cc
@@ -170,26 +170,27 @@ _hb_ot_shape_complex_setup_masks_indic (hb_ot_map_t *map, hb_buffer_t *buffer, h
   unsigned int count = buffer->len;
   for (unsigned int i = 0; i < count; i++)
   {
-    unsigned int type = get_indic_categories (buffer->info[i].codepoint);
-
-    buffer->info[i].indic_category() = type & 0x0F;
-    buffer->info[i].indic_position() = type >> 4;
-
-    if (buffer->info[i].indic_category() == OT_C) {
-      buffer->info[i].indic_position() = consonant_position (buffer->info[i].codepoint);
-      if (is_ra (buffer->info[i].codepoint))
-	buffer->info[i].indic_category() = OT_Ra;
-    } else if (buffer->info[i].indic_category() == OT_SM ||
-	       buffer->info[i].indic_category() == OT_VD) {
-      buffer->info[i].indic_position() = POS_SMVD;
-    } else if (unlikely (buffer->info[i].codepoint == 0x200C))
-      buffer->info[i].indic_category() = OT_ZWNJ;
-    else if (unlikely (buffer->info[i].codepoint == 0x200D))
-      buffer->info[i].indic_category() = OT_ZWJ;
-
-    if (unlikely (buffer->info[i].codepoint == 0x0952)) {
-      buffer->info[i].indic_category() = OT_A;
-      buffer->info[i].indic_position() = POS_SMVD;
+    hb_glyph_info_t &info = buffer->info[i];
+    unsigned int type = get_indic_categories (info.codepoint);
+
+    info.indic_category() = type & 0x0F;
+    info.indic_position() = type >> 4;
+
+    if (info.indic_category() == OT_C) {
+      info.indic_position() = consonant_position (info.codepoint);
+      if (is_ra (info.codepoint))
+	info.indic_category() = OT_Ra;
+    } else if (info.indic_category() == OT_SM ||
+	       info.indic_category() == OT_VD) {
+      info.indic_position() = POS_SMVD;
+    } else if (unlikely (info.codepoint == 0x200C))
+      info.indic_category() = OT_ZWNJ;
+    else if (unlikely (info.codepoint == 0x200D))
+      info.indic_category() = OT_ZWJ;
+
+    if (unlikely (info.codepoint == 0x0952)) {
+      info.indic_category() = OT_A;
+      info.indic_position() = POS_SMVD;
     }
   }
 }
commit dbccf87eef0d26838fa4bb3ae26410f6c4818836
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Wed May 9 17:24:39 2012 +0200

    [Indic] Make room for more reordering positions

diff --git a/src/hb-ot-shape-complex-indic-private.hh b/src/hb-ot-shape-complex-indic-private.hh
index 8ba37d3..babb1fc 100644
--- a/src/hb-ot-shape-complex-indic-private.hh
+++ b/src/hb-ot-shape-complex-indic-private.hh
@@ -62,19 +62,24 @@ enum indic_category_t {
 
 /* Visual positions in a syllable from left to right. */
 enum indic_position_t {
-  POS_LEFT_MATRA = 0,
+  POS_RA,
+  POS_LEFT_MATRA,
 
-  POS_PRE = 1,
-  POS_BASE = 2,
-  POS_ABOVE = 3,
-  POS_BELOW = 4,
-  POS_POST = 5,
+  POS_PRE_C,
 
-  POS_MATRAS = 6,
+  POS_BEFORE_HALFS,
+  POS_HALFS,
 
-  POS_REPH = 7,
+  POS_BASE_C,
+  POS_ABOVE_C,
+  POS_BELOW_C,
+  POS_POST_C,
 
-  POS_SMVD = 8
+  POS_MATRAS,
+
+  POS_REPH,
+
+  POS_SMVD
 };
 
 /* Categories used in IndicSyllabicCategory.txt from UCD */
@@ -106,7 +111,7 @@ enum indic_syllabic_category_t {
 
 /* Categories used in IndicSMatraCategory.txt from UCD */
 enum indic_matra_category_t {
-  INDIC_MATRA_CATEGORY_NOT_APPLICABLE		= POS_BASE,
+  INDIC_MATRA_CATEGORY_NOT_APPLICABLE		= POS_BASE_C,
 
   INDIC_MATRA_CATEGORY_LEFT			= POS_LEFT_MATRA,
   INDIC_MATRA_CATEGORY_TOP			= POS_MATRAS,
@@ -155,100 +160,100 @@ static const struct consonant_position_t {
   hb_codepoint_t u;
   indic_position_t position;
 } consonant_positions[] = {
-  {0x0930, POS_BELOW},
-  {0x09AC, POS_BELOW},
-  {0x09AF, POS_POST},
-  {0x09B0, POS_BELOW},
-  {0x09F0, POS_BELOW},
-  {0x0A2F, POS_POST},
-  {0x0A30, POS_BELOW},
-  {0x0A35, POS_BELOW},
-  {0x0A39, POS_BELOW},
-  {0x0AB0, POS_BELOW},
-  {0x0B24, POS_BELOW},
-  {0x0B28, POS_BELOW},
-  {0x0B2C, POS_BELOW},
-  {0x0B2D, POS_BELOW},
-  {0x0B2E, POS_BELOW},
-  {0x0B2F, POS_POST},
-  {0x0B30, POS_BELOW},
-  {0x0B32, POS_BELOW},
-  {0x0B33, POS_BELOW},
-  {0x0B5F, POS_POST},
-  {0x0B71, POS_BELOW},
-  {0x0C15, POS_BELOW},
-  {0x0C16, POS_BELOW},
-  {0x0C17, POS_BELOW},
-  {0x0C18, POS_BELOW},
-  {0x0C19, POS_BELOW},
-  {0x0C1A, POS_BELOW},
-  {0x0C1B, POS_BELOW},
-  {0x0C1C, POS_BELOW},
-  {0x0C1D, POS_BELOW},
-  {0x0C1E, POS_BELOW},
-  {0x0C1F, POS_BELOW},
-  {0x0C20, POS_BELOW},
-  {0x0C21, POS_BELOW},
-  {0x0C22, POS_BELOW},
-  {0x0C23, POS_BELOW},
-  {0x0C24, POS_BELOW},
-  {0x0C25, POS_BELOW},
-  {0x0C26, POS_BELOW},
-  {0x0C27, POS_BELOW},
-  {0x0C28, POS_BELOW},
-  {0x0C2A, POS_BELOW},
-  {0x0C2B, POS_BELOW},
-  {0x0C2C, POS_BELOW},
-  {0x0C2D, POS_BELOW},
-  {0x0C2E, POS_BELOW},
-  {0x0C2F, POS_BELOW},
-  {0x0C30, POS_BELOW},
-  {0x0C32, POS_BELOW},
-  {0x0C33, POS_BELOW},
-  {0x0C35, POS_BELOW},
-  {0x0C36, POS_BELOW},
-  {0x0C37, POS_BELOW},
-  {0x0C38, POS_BELOW},
-  {0x0C39, POS_BELOW},
-  {0x0C95, POS_BELOW},
-  {0x0C96, POS_BELOW},
-  {0x0C97, POS_BELOW},
-  {0x0C98, POS_BELOW},
-  {0x0C99, POS_BELOW},
-  {0x0C9A, POS_BELOW},
-  {0x0C9B, POS_BELOW},
-  {0x0C9C, POS_BELOW},
-  {0x0C9D, POS_BELOW},
-  {0x0C9E, POS_BELOW},
-  {0x0C9F, POS_BELOW},
-  {0x0CA0, POS_BELOW},
-  {0x0CA1, POS_BELOW},
-  {0x0CA2, POS_BELOW},
-  {0x0CA3, POS_BELOW},
-  {0x0CA4, POS_BELOW},
-  {0x0CA5, POS_BELOW},
-  {0x0CA6, POS_BELOW},
-  {0x0CA7, POS_BELOW},
-  {0x0CA8, POS_BELOW},
-  {0x0CAA, POS_BELOW},
-  {0x0CAB, POS_BELOW},
-  {0x0CAC, POS_BELOW},
-  {0x0CAD, POS_BELOW},
-  {0x0CAE, POS_BELOW},
-  {0x0CAF, POS_BELOW},
-  {0x0CB0, POS_BELOW},
-  {0x0CB2, POS_BELOW},
-  {0x0CB3, POS_BELOW},
-  {0x0CB5, POS_BELOW},
-  {0x0CB6, POS_BELOW},
-  {0x0CB7, POS_BELOW},
-  {0x0CB8, POS_BELOW},
-  {0x0CB9, POS_BELOW},
-  {0x0CDE, POS_BELOW},
-  {0x0D2F, POS_POST},
-  {0x0D30, POS_POST},
-  {0x0D32, POS_BELOW},
-  {0x0D35, POS_POST},
+  {0x0930, POS_BELOW_C},
+  {0x09AC, POS_BELOW_C},
+  {0x09AF, POS_POST_C},
+  {0x09B0, POS_BELOW_C},
+  {0x09F0, POS_BELOW_C},
+  {0x0A2F, POS_POST_C},
+  {0x0A30, POS_BELOW_C},
+  {0x0A35, POS_BELOW_C},
+  {0x0A39, POS_BELOW_C},
+  {0x0AB0, POS_BELOW_C},
+  {0x0B24, POS_BELOW_C},
+  {0x0B28, POS_BELOW_C},
+  {0x0B2C, POS_BELOW_C},
+  {0x0B2D, POS_BELOW_C},
+  {0x0B2E, POS_BELOW_C},
+  {0x0B2F, POS_POST_C},
+  {0x0B30, POS_BELOW_C},
+  {0x0B32, POS_BELOW_C},
+  {0x0B33, POS_BELOW_C},
+  {0x0B5F, POS_POST_C},
+  {0x0B71, POS_BELOW_C},
+  {0x0C15, POS_BELOW_C},
+  {0x0C16, POS_BELOW_C},
+  {0x0C17, POS_BELOW_C},
+  {0x0C18, POS_BELOW_C},
+  {0x0C19, POS_BELOW_C},
+  {0x0C1A, POS_BELOW_C},
+  {0x0C1B, POS_BELOW_C},
+  {0x0C1C, POS_BELOW_C},
+  {0x0C1D, POS_BELOW_C},
+  {0x0C1E, POS_BELOW_C},
+  {0x0C1F, POS_BELOW_C},
+  {0x0C20, POS_BELOW_C},
+  {0x0C21, POS_BELOW_C},
+  {0x0C22, POS_BELOW_C},
+  {0x0C23, POS_BELOW_C},
+  {0x0C24, POS_BELOW_C},
+  {0x0C25, POS_BELOW_C},
+  {0x0C26, POS_BELOW_C},
+  {0x0C27, POS_BELOW_C},
+  {0x0C28, POS_BELOW_C},
+  {0x0C2A, POS_BELOW_C},
+  {0x0C2B, POS_BELOW_C},
+  {0x0C2C, POS_BELOW_C},
+  {0x0C2D, POS_BELOW_C},
+  {0x0C2E, POS_BELOW_C},
+  {0x0C2F, POS_BELOW_C},
+  {0x0C30, POS_BELOW_C},
+  {0x0C32, POS_BELOW_C},
+  {0x0C33, POS_BELOW_C},
+  {0x0C35, POS_BELOW_C},
+  {0x0C36, POS_BELOW_C},
+  {0x0C37, POS_BELOW_C},
+  {0x0C38, POS_BELOW_C},
+  {0x0C39, POS_BELOW_C},
+  {0x0C95, POS_BELOW_C},
+  {0x0C96, POS_BELOW_C},
+  {0x0C97, POS_BELOW_C},
+  {0x0C98, POS_BELOW_C},
+  {0x0C99, POS_BELOW_C},
+  {0x0C9A, POS_BELOW_C},
+  {0x0C9B, POS_BELOW_C},
+  {0x0C9C, POS_BELOW_C},
+  {0x0C9D, POS_BELOW_C},
+  {0x0C9E, POS_BELOW_C},
+  {0x0C9F, POS_BELOW_C},
+  {0x0CA0, POS_BELOW_C},
+  {0x0CA1, POS_BELOW_C},
+  {0x0CA2, POS_BELOW_C},
+  {0x0CA3, POS_BELOW_C},
+  {0x0CA4, POS_BELOW_C},
+  {0x0CA5, POS_BELOW_C},
+  {0x0CA6, POS_BELOW_C},
+  {0x0CA7, POS_BELOW_C},
+  {0x0CA8, POS_BELOW_C},
+  {0x0CAA, POS_BELOW_C},
+  {0x0CAB, POS_BELOW_C},
+  {0x0CAC, POS_BELOW_C},
+  {0x0CAD, POS_BELOW_C},
+  {0x0CAE, POS_BELOW_C},
+  {0x0CAF, POS_BELOW_C},
+  {0x0CB0, POS_BELOW_C},
+  {0x0CB2, POS_BELOW_C},
+  {0x0CB3, POS_BELOW_C},
+  {0x0CB5, POS_BELOW_C},
+  {0x0CB6, POS_BELOW_C},
+  {0x0CB7, POS_BELOW_C},
+  {0x0CB8, POS_BELOW_C},
+  {0x0CB9, POS_BELOW_C},
+  {0x0CDE, POS_BELOW_C},
+  {0x0D2F, POS_POST_C},
+  {0x0D30, POS_POST_C},
+  {0x0D32, POS_BELOW_C},
+  {0x0D35, POS_POST_C},
 };
 
 /* XXX
diff --git a/src/hb-ot-shape-complex-indic.cc b/src/hb-ot-shape-complex-indic.cc
index e4ae50f..b964861 100644
--- a/src/hb-ot-shape-complex-indic.cc
+++ b/src/hb-ot-shape-complex-indic.cc
@@ -46,7 +46,7 @@ consonant_position (hb_codepoint_t u)
 					     sizeof (consonant_positions[0]),
 					     compare_codepoint);
 
-  return record ? record->position : POS_BASE;
+  return record ? record->position : POS_BASE_C;
 }
 
 static bool
@@ -189,7 +189,7 @@ _hb_ot_shape_complex_setup_masks_indic (hb_ot_map_t *map, hb_buffer_t *buffer, h
 
     if (unlikely (buffer->info[i].codepoint == 0x0952)) {
       buffer->info[i].indic_category() = OT_A;
-      buffer->info[i].indic_position() = POS_BELOW;
+      buffer->info[i].indic_position() = POS_SMVD;
     }
   }
 }
@@ -253,8 +253,8 @@ found_consonant_syllable (const hb_ot_map_t *map, hb_buffer_t *buffer, hb_mask_t
     {
       /* -> that does not have a below-base or post-base form
        * (post-base forms have to follow below-base forms), */
-      if (info[i].indic_position() != POS_BELOW &&
-	  info[i].indic_position() != POS_POST)
+      if (info[i].indic_position() != POS_BELOW_C &&
+	  info[i].indic_position() != POS_POST_C)
       {
         base = i;
 	break;
@@ -321,8 +321,8 @@ found_consonant_syllable (const hb_ot_map_t *map, hb_buffer_t *buffer, hb_mask_t
   /* Reorder characters */
 
   for (i = start; i < base; i++)
-    info[i].indic_position() = POS_PRE;
-  info[base].indic_position() = POS_BASE;
+    info[i].indic_position() = POS_PRE_C;
+  info[base].indic_position() = POS_BASE_C;
 
 
   /* Handle beginning Ra */
commit d4480ace7fdbe48aeaf77278c032b8b2ef2ebe8e
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Wed May 9 15:56:35 2012 +0200

    [Indic] Improve matra vs consonant ordering
    
    Another 1.5% down.

diff --git a/src/hb-ot-shape-complex-indic-private.hh b/src/hb-ot-shape-complex-indic-private.hh
index e60b00a..8ba37d3 100644
--- a/src/hb-ot-shape-complex-indic-private.hh
+++ b/src/hb-ot-shape-complex-indic-private.hh
@@ -62,11 +62,19 @@ enum indic_category_t {
 
 /* Visual positions in a syllable from left to right. */
 enum indic_position_t {
+  POS_LEFT_MATRA = 0,
+
   POS_PRE = 1,
   POS_BASE = 2,
   POS_ABOVE = 3,
   POS_BELOW = 4,
-  POS_POST = 5
+  POS_POST = 5,
+
+  POS_MATRAS = 6,
+
+  POS_REPH = 7,
+
+  POS_SMVD = 8
 };
 
 /* Categories used in IndicSyllabicCategory.txt from UCD */
@@ -100,10 +108,10 @@ enum indic_syllabic_category_t {
 enum indic_matra_category_t {
   INDIC_MATRA_CATEGORY_NOT_APPLICABLE		= POS_BASE,
 
-  INDIC_MATRA_CATEGORY_LEFT			= POS_PRE - 1, /* Move *before* existing "pre" chars */
-  INDIC_MATRA_CATEGORY_TOP			= POS_ABOVE,
-  INDIC_MATRA_CATEGORY_BOTTOM			= POS_BELOW,
-  INDIC_MATRA_CATEGORY_RIGHT			= POS_POST,
+  INDIC_MATRA_CATEGORY_LEFT			= POS_LEFT_MATRA,
+  INDIC_MATRA_CATEGORY_TOP			= POS_MATRAS,
+  INDIC_MATRA_CATEGORY_BOTTOM			= POS_MATRAS,
+  INDIC_MATRA_CATEGORY_RIGHT			= POS_MATRAS,
 
   /* We don't really care much about these since we decompose them
    * in the generic pre-shaping layer.  They will only be used if
@@ -114,13 +122,13 @@ enum indic_matra_category_t {
    * TODO: There are some split matras without Unicode decompositions.
    * We have to figure out what to do with them.
    */
-  INDIC_MATRA_CATEGORY_BOTTOM_AND_RIGHT		= 8 + INDIC_MATRA_CATEGORY_BOTTOM,
-  INDIC_MATRA_CATEGORY_LEFT_AND_RIGHT		= 8 + INDIC_MATRA_CATEGORY_LEFT,
-  INDIC_MATRA_CATEGORY_TOP_AND_BOTTOM		= 8 + INDIC_MATRA_CATEGORY_BOTTOM,
-  INDIC_MATRA_CATEGORY_TOP_AND_BOTTOM_AND_RIGHT	= 8 + INDIC_MATRA_CATEGORY_BOTTOM,
-  INDIC_MATRA_CATEGORY_TOP_AND_LEFT		= 8 + INDIC_MATRA_CATEGORY_LEFT,
-  INDIC_MATRA_CATEGORY_TOP_AND_LEFT_AND_RIGHT	= 8 + INDIC_MATRA_CATEGORY_LEFT,
-  INDIC_MATRA_CATEGORY_TOP_AND_RIGHT		= 8 + INDIC_MATRA_CATEGORY_RIGHT,
+  INDIC_MATRA_CATEGORY_BOTTOM_AND_RIGHT		= POS_MATRAS,
+  INDIC_MATRA_CATEGORY_LEFT_AND_RIGHT		= POS_LEFT_MATRA,
+  INDIC_MATRA_CATEGORY_TOP_AND_BOTTOM		= POS_MATRAS,
+  INDIC_MATRA_CATEGORY_TOP_AND_BOTTOM_AND_RIGHT	= POS_MATRAS,
+  INDIC_MATRA_CATEGORY_TOP_AND_LEFT		= POS_LEFT_MATRA,
+  INDIC_MATRA_CATEGORY_TOP_AND_LEFT_AND_RIGHT	= POS_LEFT_MATRA,
+  INDIC_MATRA_CATEGORY_TOP_AND_RIGHT		= POS_MATRAS,
 
   INDIC_MATRA_CATEGORY_INVISIBLE		= INDIC_MATRA_CATEGORY_NOT_APPLICABLE,
   INDIC_MATRA_CATEGORY_OVERSTRUCK		= INDIC_MATRA_CATEGORY_NOT_APPLICABLE,
diff --git a/src/hb-ot-shape-complex-indic.cc b/src/hb-ot-shape-complex-indic.cc
index 9b2732a..e4ae50f 100644
--- a/src/hb-ot-shape-complex-indic.cc
+++ b/src/hb-ot-shape-complex-indic.cc
@@ -173,7 +173,7 @@ _hb_ot_shape_complex_setup_masks_indic (hb_ot_map_t *map, hb_buffer_t *buffer, h
     unsigned int type = get_indic_categories (buffer->info[i].codepoint);
 
     buffer->info[i].indic_category() = type & 0x0F;
-    buffer->info[i].indic_position() = (type >> 4) & 7;
+    buffer->info[i].indic_position() = type >> 4;
 
     if (buffer->info[i].indic_category() == OT_C) {
       buffer->info[i].indic_position() = consonant_position (buffer->info[i].codepoint);
@@ -181,7 +181,7 @@ _hb_ot_shape_complex_setup_masks_indic (hb_ot_map_t *map, hb_buffer_t *buffer, h
 	buffer->info[i].indic_category() = OT_Ra;
     } else if (buffer->info[i].indic_category() == OT_SM ||
 	       buffer->info[i].indic_category() == OT_VD) {
-      buffer->info[i].indic_position() = POS_POST;
+      buffer->info[i].indic_position() = POS_SMVD;
     } else if (unlikely (buffer->info[i].codepoint == 0x200C))
       buffer->info[i].indic_category() = OT_ZWNJ;
     else if (unlikely (buffer->info[i].codepoint == 0x200D))
@@ -330,7 +330,7 @@ found_consonant_syllable (const hb_ot_map_t *map, hb_buffer_t *buffer, hb_mask_t
       start + 3 <= end &&
       !is_joiner (info[start + 2]))
    {
-    info[start].indic_position() = POS_POST + 1;
+    info[start].indic_position() = POS_REPH;
     info[start].mask = mask_array[RPHF];
    }
 
diff --git a/src/indic.cc b/src/indic.cc
index 6c6e05a..e00311d 100644
--- a/src/indic.cc
+++ b/src/indic.cc
@@ -40,7 +40,7 @@ main (void)
     unsigned int position = type >> 4;
 
     hb_codepoint_t a, b;
-    if ((position & 8) && !hb_unicode_decompose (funcs, u, &a, &b))
+    if (!hb_unicode_decompose (funcs, u, &a, &b))
       printf ("U+%04X\n", u);
   }
 }
commit 33c92e769563ec2a6c1249b57d8cac742eea6f88
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Wed May 9 15:41:51 2012 +0200

    [Indic] Categorize Anudatta

diff --git a/src/hb-ot-shape-complex-indic.cc b/src/hb-ot-shape-complex-indic.cc
index 8b1814a..9b2732a 100644
--- a/src/hb-ot-shape-complex-indic.cc
+++ b/src/hb-ot-shape-complex-indic.cc
@@ -182,10 +182,15 @@ _hb_ot_shape_complex_setup_masks_indic (hb_ot_map_t *map, hb_buffer_t *buffer, h
     } else if (buffer->info[i].indic_category() == OT_SM ||
 	       buffer->info[i].indic_category() == OT_VD) {
       buffer->info[i].indic_position() = POS_POST;
-    } else if (buffer->info[i].codepoint == 0x200C)
+    } else if (unlikely (buffer->info[i].codepoint == 0x200C))
       buffer->info[i].indic_category() = OT_ZWNJ;
-    else if (buffer->info[i].codepoint == 0x200D)
+    else if (unlikely (buffer->info[i].codepoint == 0x200D))
       buffer->info[i].indic_category() = OT_ZWJ;
+
+    if (unlikely (buffer->info[i].codepoint == 0x0952)) {
+      buffer->info[i].indic_category() = OT_A;
+      buffer->info[i].indic_position() = POS_BELOW;
+    }
   }
 }
 
commit 3943293a9942201d8fc8d59212fcc8cca5132e3d
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Wed May 9 15:27:56 2012 +0200

    [Indic] Add joiner test cases for Devanagari

diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-devanagari/misc/MANIFEST b/test/shaping/texts/in-tree/shaper-indic/indic/script-devanagari/misc/MANIFEST
index 29cfb2f..4ee921e 100644
--- a/test/shaping/texts/in-tree/shaper-indic/indic/script-devanagari/misc/MANIFEST
+++ b/test/shaping/texts/in-tree/shaper-indic/indic/script-devanagari/misc/MANIFEST
@@ -1 +1,2 @@
+joiners.txt
 misc.txt
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-devanagari/misc/joiners.txt b/test/shaping/texts/in-tree/shaper-indic/indic/script-devanagari/misc/joiners.txt
new file mode 100644
index 0000000..75f85cc
--- /dev/null
+++ b/test/shaping/texts/in-tree/shaper-indic/indic/script-devanagari/misc/joiners.txt
@@ -0,0 +1,19 @@
+र्ह
+र्‌ह
+र्‍ह
+ऱ्ह
+ऱ्‌ह
+ऱ्‍ह
+क्क
+क्‍
+क्‌क
+क्‍क
+क्कि
+क्‌कि
+क्‍कि
+क्ष
+क्‌ष
+क्‍ष
+द्सि
+द्‌सि
+द्‍सि
commit 19d984edaa4f86c842345a9d4150597e045e1887
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Wed May 9 15:21:13 2012 +0200

    [Indic] Make sure Reph jumps over all matras to the right
    
    Another 12 thousand failures gone! (78 to go)

diff --git a/src/hb-ot-shape-complex-indic.cc b/src/hb-ot-shape-complex-indic.cc
index 828f3db..8b1814a 100644
--- a/src/hb-ot-shape-complex-indic.cc
+++ b/src/hb-ot-shape-complex-indic.cc
@@ -325,7 +325,7 @@ found_consonant_syllable (const hb_ot_map_t *map, hb_buffer_t *buffer, hb_mask_t
       start + 3 <= end &&
       !is_joiner (info[start + 2]))
    {
-    info[start].indic_position() = POS_POST;
+    info[start].indic_position() = POS_POST + 1;
     info[start].mask = mask_array[RPHF];
    }
 
commit 9034641333d7bfb41a0784cce72e43591faea083
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Wed May 9 15:04:58 2012 +0200

    [Indic] Keep Vedic signs at the right too

diff --git a/src/hb-ot-shape-complex-indic.cc b/src/hb-ot-shape-complex-indic.cc
index f198fba..828f3db 100644
--- a/src/hb-ot-shape-complex-indic.cc
+++ b/src/hb-ot-shape-complex-indic.cc
@@ -179,7 +179,8 @@ _hb_ot_shape_complex_setup_masks_indic (hb_ot_map_t *map, hb_buffer_t *buffer, h
       buffer->info[i].indic_position() = consonant_position (buffer->info[i].codepoint);
       if (is_ra (buffer->info[i].codepoint))
 	buffer->info[i].indic_category() = OT_Ra;
-    } else if (buffer->info[i].indic_category() == OT_SM) {
+    } else if (buffer->info[i].indic_category() == OT_SM ||
+	       buffer->info[i].indic_category() == OT_VD) {
       buffer->info[i].indic_position() = POS_POST;
     } else if (buffer->info[i].codepoint == 0x200C)
       buffer->info[i].indic_category() = OT_ZWNJ;
commit d1deaa2f5bd028e8076265cba92cffa4fa2834ac
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Wed May 9 15:04:13 2012 +0200

    Replace zerowidth invisible chars with a zero-advance space glyph
    
    Like Uniscribe does.

diff --git a/src/hb-ot-shape-complex-arabic.cc b/src/hb-ot-shape-complex-arabic.cc
index 880a6b9..746f2d7 100644
--- a/src/hb-ot-shape-complex-arabic.cc
+++ b/src/hb-ot-shape-complex-arabic.cc
@@ -25,6 +25,7 @@
  */
 
 #include "hb-ot-shape-complex-private.hh"
+#include "hb-ot-shape-private.hh"
 
 
 
@@ -248,7 +249,7 @@ _hb_ot_shape_complex_setup_masks_arabic (hb_ot_map_t *map, hb_buffer_t *buffer,
 
   for (unsigned int i = 0; i < count; i++)
   {
-    unsigned int this_type = get_joining_type (buffer->info[i].codepoint, (hb_unicode_general_category_t) buffer->info[i].general_category());
+    unsigned int this_type = get_joining_type (buffer->info[i].codepoint, _hb_glyph_info_get_general_category (&buffer->info[i]));
 
     if (unlikely (this_type == JOINING_TYPE_T)) {
       buffer->info[i].arabic_shaping_action() = NONE;
diff --git a/src/hb-ot-shape-complex-indic.cc b/src/hb-ot-shape-complex-indic.cc
index 6f14777..f198fba 100644
--- a/src/hb-ot-shape-complex-indic.cc
+++ b/src/hb-ot-shape-complex-indic.cc
@@ -433,24 +433,6 @@ found_non_indic (const hb_ot_map_t *map, hb_buffer_t *buffer, hb_mask_t *mask_ar
 #include "hb-ot-shape-complex-indic-machine.hh"
 
 static void
-remove_joiners (hb_buffer_t *buffer)
-{
-  /* For now we remove joiners.  However, Uniscbire seems to keep them
-   * and output a zero-width space glyph for them.  It is not clear to
-   * me how that is supposed to interact with GSUB. */
-
-  buffer->clear_output ();
-  unsigned int count = buffer->len;
-  for (buffer->idx = 0; buffer->idx < count;)
-    if (unlikely (is_joiner (buffer->info[buffer->idx])))
-      buffer->skip_glyph ();
-    else
-      buffer->next_glyph ();
-
-  buffer->swap_buffers ();
-}
-
-static void
 initial_reordering (const hb_ot_map_t *map,
 		    hb_face_t *face,
 		    hb_buffer_t *buffer,
@@ -462,8 +444,6 @@ initial_reordering (const hb_ot_map_t *map,
     mask_array[i] = map->get_1_mask (indic_basic_features[i].tag);
 
   find_syllables (map, buffer, mask_array);
-
-  remove_joiners (buffer);
 }
 
 static void
diff --git a/src/hb-ot-shape-complex-private.hh b/src/hb-ot-shape-complex-private.hh
index 38edaa0..3f99781 100644
--- a/src/hb-ot-shape-complex-private.hh
+++ b/src/hb-ot-shape-complex-private.hh
@@ -35,8 +35,8 @@
 
 
 /* buffer var allocations, used during the entire shaping process */
-#define general_category() var1.u8[0] /* unicode general_category (hb_unicode_general_category_t) */
-#define combining_class() var1.u8[1] /* unicode combining_class (uint8_t) */
+#define unicode_props0()	var1.u8[0]
+#define unicode_props1()	var1.u8[1]
 
 /* buffer var allocations, used by complex shapers */
 #define complex_var_persistent_u8_0()	var2.u8[0]
diff --git a/src/hb-ot-shape-normalize.cc b/src/hb-ot-shape-normalize.cc
index 0bcf7f5..4a378a8 100644
--- a/src/hb-ot-shape-normalize.cc
+++ b/src/hb-ot-shape-normalize.cc
@@ -68,19 +68,12 @@
  *     matra for the Indic shaper.
  */
 
-static inline void
-set_unicode_props (hb_glyph_info_t *info, hb_unicode_funcs_t *unicode)
-{
-  info->general_category() = hb_unicode_general_category (unicode, info->codepoint);
-  info->combining_class() = _hb_unicode_modified_combining_class (unicode, info->codepoint);
-}
-
 static void
 output_glyph (hb_font_t *font, hb_buffer_t *buffer,
 	      hb_codepoint_t glyph)
 {
   buffer->output_glyph (glyph);
-  set_unicode_props (&buffer->out_info[buffer->out_len - 1], buffer->unicode);
+  _hb_glyph_info_set_unicode_props (&buffer->out_info[buffer->out_len - 1], buffer->unicode);
 }
 
 static bool
@@ -163,8 +156,8 @@ decompose_multi_char_cluster (hb_font_t *font, hb_buffer_t *buffer,
 static int
 compare_combining_class (const hb_glyph_info_t *pa, const hb_glyph_info_t *pb)
 {
-  unsigned int a = pa->combining_class();
-  unsigned int b = pb->combining_class();
+  unsigned int a = _hb_glyph_info_get_modified_combining_class (pa);
+  unsigned int b = _hb_glyph_info_get_modified_combining_class (pb);
 
   return a < b ? -1 : a == b ? 0 : +1;
 }
@@ -214,12 +207,12 @@ _hb_ot_shape_normalize (hb_font_t *font, hb_buffer_t *buffer,
   count = buffer->len;
   for (unsigned int i = 0; i < count; i++)
   {
-    if (buffer->info[i].combining_class() == 0)
+    if (_hb_glyph_info_get_modified_combining_class (&buffer->info[i]) == 0)
       continue;
 
     unsigned int end;
     for (end = i + 1; end < count; end++)
-      if (buffer->info[end].combining_class() == 0)
+      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
@@ -254,11 +247,11 @@ _hb_ot_shape_normalize (hb_font_t *font, hb_buffer_t *buffer,
     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 ||
-	 buffer->info[buffer->idx].combining_class() != 0) &&
+	 _hb_glyph_info_get_modified_combining_class (&buffer->info[buffer->idx]) != 0) &&
 	/* If there's anything between the starter and this char, they should have CCC
 	 * smaller than this character's. */
 	(starter == buffer->out_len - 1 ||
-	 buffer->out_info[buffer->out_len - 1].combining_class() < buffer->info[buffer->idx].combining_class()) &&
+	 _hb_glyph_info_get_modified_combining_class (&buffer->out_info[buffer->out_len - 1]) < _hb_glyph_info_get_modified_combining_class (&buffer->info[buffer->idx])) &&
 	/* And compose. */
 	hb_unicode_compose (buffer->unicode,
 			    buffer->out_info[starter].codepoint,
@@ -270,7 +263,7 @@ _hb_ot_shape_normalize (hb_font_t *font, hb_buffer_t *buffer,
       /* Composes. Modify starter and carry on. */
       buffer->out_info[starter].codepoint = composed;
       /* XXX update cluster */
-      set_unicode_props (&buffer->out_info[starter], buffer->unicode);
+      _hb_glyph_info_set_unicode_props (&buffer->out_info[starter], buffer->unicode);
 
       buffer->skip_glyph ();
       continue;
@@ -279,7 +272,7 @@ _hb_ot_shape_normalize (hb_font_t *font, hb_buffer_t *buffer,
     /* Blocked, or doesn't compose. */
     buffer->next_glyph ();
 
-    if (buffer->out_info[buffer->out_len - 1].combining_class() == 0)
+    if (_hb_glyph_info_get_modified_combining_class (&buffer->out_info[buffer->out_len - 1]) == 0)
       starter = buffer->out_len - 1;
   }
   buffer->swap_buffers ();
diff --git a/src/hb-ot-shape-private.hh b/src/hb-ot-shape-private.hh
index 5fc69b1..df0c705 100644
--- a/src/hb-ot-shape-private.hh
+++ b/src/hb-ot-shape-private.hh
@@ -53,4 +53,31 @@ _hb_ot_shape (hb_font_t          *font,
 	      const hb_feature_t *features,
 	      unsigned int        num_features);
 
+
+inline void
+_hb_glyph_info_set_unicode_props (hb_glyph_info_t *info, hb_unicode_funcs_t *unicode)
+{
+  info->unicode_props0() = ((unsigned int) hb_unicode_general_category (unicode, info->codepoint)) |
+			   (_hb_unicode_is_zero_width (info->codepoint) ? 0x80 : 0);
+  info->unicode_props1() = _hb_unicode_modified_combining_class (unicode, info->codepoint);
+}
+
+inline hb_unicode_general_category_t
+_hb_glyph_info_get_general_category (const hb_glyph_info_t *info)
+{
+  return (hb_unicode_general_category_t) (info->unicode_props0() & 0x7F);
+}
+
+inline unsigned int
+_hb_glyph_info_get_modified_combining_class (const hb_glyph_info_t *info)
+{
+  return info->unicode_props1();
+}
+
+inline hb_bool_t
+_hb_glyph_info_is_zero_width (const hb_glyph_info_t *info)
+{
+  return !!(info->unicode_props0() & 0x80);
+}
+
 #endif /* HB_OT_SHAPE_PRIVATE_HH */
diff --git a/src/hb-ot-shape.cc b/src/hb-ot-shape.cc
index 167b1d7..dbfcf18 100644
--- a/src/hb-ot-shape.cc
+++ b/src/hb-ot-shape.cc
@@ -43,6 +43,7 @@ hb_tag_t common_features[] = {
   HB_TAG('r','l','i','g'),
 };
 
+
 hb_tag_t horizontal_features[] = {
   HB_TAG('c','a','l','t'),
   HB_TAG('c','l','i','g'),
@@ -170,19 +171,12 @@ hb_ot_shape_setup_masks (hb_ot_shape_context_t *c)
 
 /* Prepare */
 
-static inline void
-set_unicode_props (hb_glyph_info_t *info, hb_unicode_funcs_t *unicode)
-{
-  info->general_category() = hb_unicode_general_category (unicode, info->codepoint);
-  info->combining_class() = _hb_unicode_modified_combining_class (unicode, info->codepoint);
-}
-
 static void
 hb_set_unicode_props (hb_buffer_t *buffer)
 {
   unsigned int count = buffer->len;
   for (unsigned int i = 0; i < count; i++)
-    set_unicode_props (&buffer->info[i], buffer->unicode);
+    _hb_glyph_info_set_unicode_props (&buffer->info[i], buffer->unicode);
 }
 
 static void
@@ -190,7 +184,7 @@ hb_form_clusters (hb_buffer_t *buffer)
 {
   unsigned int count = buffer->len;
   for (unsigned int i = 1; i < count; i++)
-    if (FLAG (buffer->info[i].general_category()) &
+    if (FLAG (_hb_glyph_info_get_general_category (&buffer->info[i])) &
 	(FLAG (HB_UNICODE_GENERAL_CATEGORY_SPACING_MARK) |
 	 FLAG (HB_UNICODE_GENERAL_CATEGORY_ENCLOSING_MARK) |
 	 FLAG (HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK)))
@@ -379,6 +373,23 @@ hb_position_complex_fallback_visual (hb_ot_shape_context_t *c)
   hb_truetype_kern (c);
 }
 
+static void
+hb_hide_zerowidth (hb_ot_shape_context_t *c)
+{
+  /* TODO Save the space character in the font? */
+  hb_codepoint_t space;
+  if (!hb_font_get_glyph (c->font, ' ', 0, &space))
+    return; /* No point! */
+
+  unsigned int count = c->buffer->len;
+  for (unsigned int i = 0; i < count; i++)
+    if (unlikely (_hb_glyph_info_is_zero_width (&c->buffer->info[i]))) {
+      c->buffer->info[i].codepoint = space;
+      c->buffer->pos[i].x_advance = 0;
+      c->buffer->pos[i].y_advance = 0;
+    }
+}
+
 
 /* Do it! */
 
@@ -390,10 +401,10 @@ hb_ot_shape_execute_internal (hb_ot_shape_context_t *c)
   /* Save the original direction, we use it later. */
   c->target_direction = c->buffer->props.direction;
 
-  HB_BUFFER_ALLOCATE_VAR (c->buffer, general_category);
-  HB_BUFFER_ALLOCATE_VAR (c->buffer, combining_class);
+  HB_BUFFER_ALLOCATE_VAR (c->buffer, unicode_props0);
+  HB_BUFFER_ALLOCATE_VAR (c->buffer, unicode_props1);
 
-  hb_set_unicode_props (c->buffer); /* BUFFER: Set general_category and combining_class */
+  hb_set_unicode_props (c->buffer);
 
   hb_form_clusters (c->buffer);
 
@@ -427,8 +438,10 @@ hb_ot_shape_execute_internal (hb_ot_shape_context_t *c)
       hb_position_complex_fallback_visual (c);
   }
 
-  HB_BUFFER_DEALLOCATE_VAR (c->buffer, combining_class);
-  HB_BUFFER_DEALLOCATE_VAR (c->buffer, general_category);
+  hb_hide_zerowidth (c);
+
+  HB_BUFFER_DEALLOCATE_VAR (c->buffer, unicode_props1);
+  HB_BUFFER_DEALLOCATE_VAR (c->buffer, unicode_props0);
 
   c->buffer->props.direction = c->target_direction;
 
diff --git a/src/hb-unicode-private.hh b/src/hb-unicode-private.hh
index ad85be7..c06dfe5 100644
--- a/src/hb-unicode-private.hh
+++ b/src/hb-unicode-private.hh
@@ -114,5 +114,43 @@ _hb_unicode_is_variation_selector (hb_codepoint_t unicode)
 		   (unicode >= 0xE0100 && unicode <= 0xE01EF));  /* VARIATION SELECTOR-17..256 */
 }
 
+/* Zero-Width invisible characters:
+ *
+ *  00AD  SOFT HYPHEN
+ *  034F  COMBINING GRAPHEME JOINER
+ *
+ *  200B  ZERO WIDTH SPACE
+ *  200C  ZERO WIDTH NON-JOINER
+ *  200D  ZERO WIDTH JOINER
+ *  200E  LEFT-TO-RIGHT MARK
+ *  200F  RIGHT-TO-LEFT MARK
+ *
+ *  2028  LINE SEPARATOR
+ *
+ *  202A  LEFT-TO-RIGHT EMBEDDING
+ *  202B  RIGHT-TO-LEFT EMBEDDING
+ *  202C  POP DIRECTIONAL FORMATTING
+ *  202D  LEFT-TO-RIGHT OVERRIDE
+ *  202E  RIGHT-TO-LEFT OVERRIDE
+ *
+ *  2060  WORD JOINER
+ *  2061  FUNCTION APPLICATION
+ *  2062  INVISIBLE TIMES
+ *  2063  INVISIBLE SEPARATOR
+ *
+ *  FEFF  ZERO WIDTH NO-BREAK SPACE
+ */
+static inline hb_bool_t
+_hb_unicode_is_zero_width (hb_codepoint_t ch)
+{
+  return ((ch & ~0x007F) == 0x2000 && (
+	  (ch >= 0x200B && ch <= 0x200F) ||
+	  (ch >= 0x202A && ch <= 0x202E) ||
+	  (ch >= 0x2060 && ch <= 0x2063) ||
+	  (ch == 0x2028)
+	 )) || unlikely (ch == 0x00AD
+		      || ch == 0x034F
+		      || ch == 0xFEFF);
+}
 
 #endif /* HB_UNICODE_PRIVATE_HH */
commit 49e5da1591b8d28f01e7ff9caac9d9ac53668bba
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Wed May 9 13:23:27 2012 +0200

    [indic] Keep the syllable modifier marks to the right
    
    Shaping failures on Hindi Wikipedia go down from 25% to 14%!

diff --git a/src/hb-ot-shape-complex-indic.cc b/src/hb-ot-shape-complex-indic.cc
index f8ff4ac..6f14777 100644
--- a/src/hb-ot-shape-complex-indic.cc
+++ b/src/hb-ot-shape-complex-indic.cc
@@ -179,6 +179,8 @@ _hb_ot_shape_complex_setup_masks_indic (hb_ot_map_t *map, hb_buffer_t *buffer, h
       buffer->info[i].indic_position() = consonant_position (buffer->info[i].codepoint);
       if (is_ra (buffer->info[i].codepoint))
 	buffer->info[i].indic_category() = OT_Ra;
+    } else if (buffer->info[i].indic_category() == OT_SM) {
+      buffer->info[i].indic_position() = POS_POST;
     } else if (buffer->info[i].codepoint == 0x200C)
       buffer->info[i].indic_category() = OT_ZWNJ;
     else if (buffer->info[i].codepoint == 0x200D)
commit 5b1260909350bffa3e3d06da346f9f86ce651dbb
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Wed May 9 12:37:27 2012 +0200

    Minor

diff --git a/src/hb-ot-shape-complex-private.hh b/src/hb-ot-shape-complex-private.hh
index 6a868d9..38edaa0 100644
--- a/src/hb-ot-shape-complex-private.hh
+++ b/src/hb-ot-shape-complex-private.hh
@@ -136,7 +136,7 @@ hb_ot_shape_complex_categorize (const hb_segment_properties_t *props)
     /* Simple */
 
     /* Unicode-1.1 additions */
-    /* TODO These two need their own shaper I guess? */
+    /* These have their own shaper now. */
     case HB_SCRIPT_LAO:
     case HB_SCRIPT_THAI:
 
commit 9ce939232bbce8f51e235195e3854d1e8bb961f8
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Wed May 9 12:03:09 2012 +0200

    Minor

diff --git a/src/hb-ot-shape-complex-indic-private.hh b/src/hb-ot-shape-complex-indic-private.hh
index b7bce17..e60b00a 100644
--- a/src/hb-ot-shape-complex-indic-private.hh
+++ b/src/hb-ot-shape-complex-indic-private.hh
@@ -114,13 +114,13 @@ enum indic_matra_category_t {
    * TODO: There are some split matras without Unicode decompositions.
    * We have to figure out what to do with them.
    */
-  INDIC_MATRA_CATEGORY_BOTTOM_AND_RIGHT		= 8 | INDIC_MATRA_CATEGORY_BOTTOM,
-  INDIC_MATRA_CATEGORY_LEFT_AND_RIGHT		= 8 | INDIC_MATRA_CATEGORY_LEFT,
-  INDIC_MATRA_CATEGORY_TOP_AND_BOTTOM		= 8 | INDIC_MATRA_CATEGORY_BOTTOM,
-  INDIC_MATRA_CATEGORY_TOP_AND_BOTTOM_AND_RIGHT	= 8 | INDIC_MATRA_CATEGORY_BOTTOM,
-  INDIC_MATRA_CATEGORY_TOP_AND_LEFT		= 8 | INDIC_MATRA_CATEGORY_LEFT,
-  INDIC_MATRA_CATEGORY_TOP_AND_LEFT_AND_RIGHT	= 8 | INDIC_MATRA_CATEGORY_LEFT,
-  INDIC_MATRA_CATEGORY_TOP_AND_RIGHT		= 8 | INDIC_MATRA_CATEGORY_RIGHT,
+  INDIC_MATRA_CATEGORY_BOTTOM_AND_RIGHT		= 8 + INDIC_MATRA_CATEGORY_BOTTOM,
+  INDIC_MATRA_CATEGORY_LEFT_AND_RIGHT		= 8 + INDIC_MATRA_CATEGORY_LEFT,
+  INDIC_MATRA_CATEGORY_TOP_AND_BOTTOM		= 8 + INDIC_MATRA_CATEGORY_BOTTOM,
+  INDIC_MATRA_CATEGORY_TOP_AND_BOTTOM_AND_RIGHT	= 8 + INDIC_MATRA_CATEGORY_BOTTOM,
+  INDIC_MATRA_CATEGORY_TOP_AND_LEFT		= 8 + INDIC_MATRA_CATEGORY_LEFT,
+  INDIC_MATRA_CATEGORY_TOP_AND_LEFT_AND_RIGHT	= 8 + INDIC_MATRA_CATEGORY_LEFT,
+  INDIC_MATRA_CATEGORY_TOP_AND_RIGHT		= 8 + INDIC_MATRA_CATEGORY_RIGHT,
 
   INDIC_MATRA_CATEGORY_INVISIBLE		= INDIC_MATRA_CATEGORY_NOT_APPLICABLE,
   INDIC_MATRA_CATEGORY_OVERSTRUCK		= INDIC_MATRA_CATEGORY_NOT_APPLICABLE,
commit 76b3409de6887c1cdd5c679939497b1b56f4554b
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Wed May 9 11:43:43 2012 +0200

    [indic] Better Reph matching

diff --git a/src/hb-ot-shape-complex-indic.cc b/src/hb-ot-shape-complex-indic.cc
index a9aa232..f8ff4ac 100644
--- a/src/hb-ot-shape-complex-indic.cc
+++ b/src/hb-ot-shape-complex-indic.cc
@@ -220,14 +220,24 @@ found_consonant_syllable (const hb_ot_map_t *map, hb_buffer_t *buffer, hb_mask_t
    */
 
   unsigned int base = end;
+  bool has_reph = false;
 
-  /* -> starting from the end of the syllable, move backwards */
-  i = end;
+  /* -> If the syllable starts with Ra + Halant (in a script that has Reph)
+   *    and has more than one consonant, Ra is excluded from candidates for
+   *    base consonants. */
   unsigned int limit = start;
-  if (info[start].indic_category() == OT_Ra && start + 2 <= end) {
+  if (mask_array[RPHF] &&
+      start + 2 < end &&
+      info[start].indic_category() == OT_Ra &&
+      info[start + 1].indic_category() == OT_H)
+  {
     limit += 2;
     base = start;
+    has_reph = true;
   };
+
+  /* -> starting from the end of the syllable, move backwards */
+  i = end;
   do {
     i--;
     /* -> until a consonant is found */
@@ -308,9 +318,8 @@ found_consonant_syllable (const hb_ot_map_t *map, hb_buffer_t *buffer, hb_mask_t
 
 
   /* Handle beginning Ra */
-  if (start + 3 <= end &&
-      info[start].indic_category() == OT_Ra &&
-      info[start + 1].indic_category() == OT_H &&
+  if (has_reph &&
+      start + 3 <= end &&
       !is_joiner (info[start + 2]))
    {
     info[start].indic_position() = POS_POST;
commit df6d45c693c417bf311e6fa49f18a8558542e525
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Wed May 9 11:38:31 2012 +0200

    Minor

diff --git a/src/hb-ot-shape-complex-indic.cc b/src/hb-ot-shape-complex-indic.cc
index fcd4ba9..a9aa232 100644
--- a/src/hb-ot-shape-complex-indic.cc
+++ b/src/hb-ot-shape-complex-indic.cc
@@ -92,7 +92,7 @@ enum {
   _NUKT,
   AKHN,
   RPHF,
-  RKRF,
+  _RKRF,
   PREF,
   BLWF,
   HALF,
commit 412b91889d9a1ae477e8b6907d0b9a76e78a6c91
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Wed May 9 11:07:18 2012 +0200

    [indic] Apply Indic features in order

diff --git a/src/hb-ot-shape-complex-indic.cc b/src/hb-ot-shape-complex-indic.cc
index 95e04ab..fcd4ba9 100644
--- a/src/hb-ot-shape-complex-indic.cc
+++ b/src/hb-ot-shape-complex-indic.cc
@@ -136,13 +136,17 @@ _hb_ot_shape_complex_collect_features_indic (hb_ot_map_builder_t *map, const hb_
 
   map->add_gsub_pause (initial_reordering, NULL);
 
-  for (unsigned int i = 0; i < ARRAY_LENGTH (indic_basic_features); i++)
+  for (unsigned int i = 0; i < ARRAY_LENGTH (indic_basic_features); i++) {
     map->add_bool_feature (indic_basic_features[i].tag, indic_basic_features[i].is_global);
+    map->add_gsub_pause (NULL, NULL);
+  }
 
   map->add_gsub_pause (final_reordering, NULL);
 
-  for (unsigned int i = 0; i < ARRAY_LENGTH (indic_other_features); i++)
+  for (unsigned int i = 0; i < ARRAY_LENGTH (indic_other_features); i++) {
     map->add_bool_feature (indic_other_features[i], true);
+    map->add_gsub_pause (NULL, NULL);
+  }
 }
 
 
commit 1ac075b227090a9ad930dcc1670236c176b27067
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Wed May 9 11:06:47 2012 +0200

    [indic] Apply rakaar forms
    
    Fixes 10% of the failures against all of Hindi Wikipedia!

diff --git a/src/hb-ot-shape-complex-indic.cc b/src/hb-ot-shape-complex-indic.cc
index 09222f7..95e04ab 100644
--- a/src/hb-ot-shape-complex-indic.cc
+++ b/src/hb-ot-shape-complex-indic.cc
@@ -78,7 +78,7 @@ static const struct {
   {HB_TAG('n','u','k','t'), true},
   {HB_TAG('a','k','h','n'), false},
   {HB_TAG('r','p','h','f'), false},
-  {HB_TAG('r','k','r','f'), false},
+  {HB_TAG('r','k','r','f'), true},
   {HB_TAG('p','r','e','f'), false},
   {HB_TAG('b','l','w','f'), false},
   {HB_TAG('h','a','l','f'), false},



More information about the HarfBuzz mailing list