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

Behdad Esfahbod behdad at kemper.freedesktop.org
Tue Aug 7 22:21:14 PDT 2012


 src/Makefile.am                              |    2 
 src/hb-font-private.hh                       |   43 ++++
 src/hb-font.cc                               |   24 +-
 src/hb-font.h                                |   14 +
 src/hb-ft.cc                                 |    3 
 src/hb-graphite2.cc                          |   60 ++---
 src/hb-icu.cc                                |    6 
 src/hb-mutex-private.hh                      |    1 
 src/hb-old.cc                                |    2 
 src/hb-old/harfbuzz-shaper.cpp               |    2 
 src/hb-ot-layout-gsub-table.hh               |   26 ++
 src/hb-ot-layout-gsubgpos-private.hh         |   37 +--
 src/hb-ot-layout.cc                          |    3 
 src/hb-ot-shape-normalize.cc                 |  186 +++++++++++++++++-
 src/hb-ot-shape-position-fallback-private.hh |   39 +++
 src/hb-ot-shape-position-fallback.cc         |  276 +++++++++++++++++++++++++++
 src/hb-ot-shape.cc                           |   26 --
 src/hb-private.hh                            |   16 +
 src/hb-unicode-private.hh                    |  124 ++++++++----
 src/hb-unicode.cc                            |  107 +++-------
 src/hb-uniscribe.cc                          |    1 
 src/hb-uniscribe.h                           |    2 
 src/test-would-substitute.cc                 |   11 -
 util/options.cc                              |    2 
 24 files changed, 808 insertions(+), 205 deletions(-)

New commits:
commit 21756934a15e31dc243e2b6d80adec5752477652
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Wed Aug 8 01:20:45 2012 -0400

    [OT] Implement fallback positioning
    
    Implemented for Arabic, Hebrew, and generic marks.
    Activated if no GPOS table present.

diff --git a/src/hb-font.h b/src/hb-font.h
index 0fde258..4a043be 100644
--- a/src/hb-font.h
+++ b/src/hb-font.h
@@ -143,7 +143,8 @@ hb_font_funcs_make_immutable (hb_font_funcs_t *ffuncs);
 hb_bool_t
 hb_font_funcs_is_immutable (hb_font_funcs_t *ffuncs);
 
-/* funcs */
+
+/* glyph extents */
 
 typedef struct hb_glyph_extents_t
 {
diff --git a/src/hb-ft.cc b/src/hb-ft.cc
index 3717269..9ac556e 100644
--- a/src/hb-ft.cc
+++ b/src/hb-ft.cc
@@ -201,7 +201,7 @@ hb_ft_get_glyph_extents (hb_font_t *font HB_UNUSED,
   extents->x_bearing = ft_face->glyph->metrics.horiBearingX;
   extents->y_bearing = ft_face->glyph->metrics.horiBearingY;
   extents->width = ft_face->glyph->metrics.width;
-  extents->height = ft_face->glyph->metrics.height;
+  extents->height = -ft_face->glyph->metrics.height;
   return true;
 }
 
@@ -464,6 +464,7 @@ hb_ft_font_set_funcs (hb_font_t *font)
 
   FT_Select_Charmap (ft_face, FT_ENCODING_UNICODE);
 
+  assert (font->y_scale >= 0);
   FT_Set_Char_Size (ft_face,
 		    font->x_scale, font->y_scale,
 		    0, 0);
diff --git a/src/hb-old.cc b/src/hb-old.cc
index 1d5544b..ec25e52 100644
--- a/src/hb-old.cc
+++ b/src/hb-old.cc
@@ -152,7 +152,7 @@ hb_old_getGlyphMetrics (HB_Font old_font,
   metrics->x       = extents.x_bearing;
   metrics->y       = extents.y_bearing;
   metrics->width   = extents.width;
-  metrics->height  = -extents.height;
+  metrics->height  = extents.height;
   metrics->xOffset = font->get_glyph_h_advance (glyph);
   metrics->yOffset = 0;
 }
diff --git a/src/hb-ot-shape-position-fallback.cc b/src/hb-ot-shape-position-fallback.cc
index 13cebe7..7966852 100644
--- a/src/hb-ot-shape-position-fallback.cc
+++ b/src/hb-ot-shape-position-fallback.cc
@@ -27,10 +27,11 @@
 #include "hb-ot-shape-position-fallback-private.hh"
 
 static void
-hb_zero_mark_advances (hb_buffer_t *buffer)
+zero_mark_advances (hb_buffer_t *buffer,
+		    unsigned int start,
+		    unsigned int end)
 {
-  unsigned int count = buffer->len;
-  for (unsigned int i = 0; i < count; i++)
+  for (unsigned int i = start; i < end; i++)
     if (_hb_glyph_info_get_general_category (&buffer->info[i]) == HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK)
     {
       buffer->pos[i].x_advance = 0;
@@ -38,10 +39,238 @@ hb_zero_mark_advances (hb_buffer_t *buffer)
     }
 }
 
+static unsigned int
+recategorize_combining_class (unsigned int modified_combining_class)
+{
+  if (modified_combining_class >= 200)
+    return modified_combining_class;
+
+  /* This should be kept in sync with modified combining class mapping
+   * from hb-unicode.cc. */
+  switch (modified_combining_class)
+  {
+    /* Hebrew */
+    case HB_MODIFIED_COMBINING_CLASS_CCC10: /* sheva */
+    case HB_MODIFIED_COMBINING_CLASS_CCC11: /* hataf segol */
+    case HB_MODIFIED_COMBINING_CLASS_CCC12: /* hataf patah */
+    case HB_MODIFIED_COMBINING_CLASS_CCC13: /* hataf qamats */
+    case HB_MODIFIED_COMBINING_CLASS_CCC14: /* hiriq */
+    case HB_MODIFIED_COMBINING_CLASS_CCC15: /* tsere */
+    case HB_MODIFIED_COMBINING_CLASS_CCC16: /* segol */
+    case HB_MODIFIED_COMBINING_CLASS_CCC17: /* patah */
+    case HB_MODIFIED_COMBINING_CLASS_CCC18: /* qamats */
+    case HB_MODIFIED_COMBINING_CLASS_CCC20: /* qubuts */
+    case HB_MODIFIED_COMBINING_CLASS_CCC22: /* meteg */
+      return HB_UNICODE_COMBINING_CLASS_BELOW;
+
+    case HB_MODIFIED_COMBINING_CLASS_CCC23: /* rafe */
+      return HB_UNICODE_COMBINING_CLASS_ATTACHED_ABOVE;
+
+    case HB_MODIFIED_COMBINING_CLASS_CCC24: /* shin dot */
+      return HB_UNICODE_COMBINING_CLASS_ABOVE_RIGHT;
+
+    case HB_MODIFIED_COMBINING_CLASS_CCC25: /* sin dot */
+    case HB_MODIFIED_COMBINING_CLASS_CCC19: /* holam */
+      return HB_UNICODE_COMBINING_CLASS_ABOVE_LEFT;
+
+    case HB_MODIFIED_COMBINING_CLASS_CCC26: /* point varika */
+      return HB_UNICODE_COMBINING_CLASS_ABOVE;
+
+    case HB_MODIFIED_COMBINING_CLASS_CCC21: /* dagesh */
+      break;
+
+    /* Arabic and Syriac */
+    case HB_MODIFIED_COMBINING_CLASS_CCC27: /* fathatan */
+    case HB_MODIFIED_COMBINING_CLASS_CCC28: /* dammatan */
+    case HB_MODIFIED_COMBINING_CLASS_CCC30: /* fatha */
+    case HB_MODIFIED_COMBINING_CLASS_CCC31: /* damma */
+    case HB_MODIFIED_COMBINING_CLASS_CCC33: /* shadda */
+    case HB_MODIFIED_COMBINING_CLASS_CCC34: /* sukun */
+    case HB_MODIFIED_COMBINING_CLASS_CCC35: /* superscript alef */
+    case HB_MODIFIED_COMBINING_CLASS_CCC36: /* superscript alaph */
+      return HB_UNICODE_COMBINING_CLASS_ABOVE;
+
+    case HB_MODIFIED_COMBINING_CLASS_CCC29: /* kasratan */
+    case HB_MODIFIED_COMBINING_CLASS_CCC32: /* kasra */
+      return HB_UNICODE_COMBINING_CLASS_BELOW;
+
+    /* TODO Thai, Lao, and Tibetan */
+  }
+
+  return modified_combining_class;
+}
+
+static inline void
+position_mark (const hb_ot_shape_plan_t *plan,
+	       hb_font_t *font,
+	       hb_buffer_t  *buffer,
+	       hb_glyph_extents_t &base_extents,
+	       unsigned int i,
+	       unsigned int combining_class)
+{
+  hb_glyph_extents_t mark_extents;
+  if (!hb_font_get_glyph_extents (font,
+				  buffer->info[i].codepoint,
+				  &mark_extents))
+    return;
+
+  hb_position_t y_gap = font->y_scale / 16;
+
+  hb_glyph_position_t &pos = buffer->pos[i];
+  pos.x_offset = pos.y_offset = 0;
+
+
+  /* We dont position LEFT and RIGHT marks. */
+
+  /* X positioning */
+  switch (combining_class)
+  {
+    case HB_UNICODE_COMBINING_CLASS_DOUBLE_BELOW:
+    case HB_UNICODE_COMBINING_CLASS_DOUBLE_ABOVE:
+      /* TODO Do something...  For now, fall through. */
+
+    case HB_UNICODE_COMBINING_CLASS_ATTACHED_BELOW:
+    case HB_UNICODE_COMBINING_CLASS_ATTACHED_ABOVE:
+    case HB_UNICODE_COMBINING_CLASS_BELOW:
+    case HB_UNICODE_COMBINING_CLASS_ABOVE:
+      /* Center align. */
+      pos.x_offset += base_extents.x_bearing + (base_extents.width - mark_extents.width) / 2 - mark_extents.x_bearing;
+      break;
+
+    case HB_UNICODE_COMBINING_CLASS_ATTACHED_BELOW_LEFT:
+    case HB_UNICODE_COMBINING_CLASS_BELOW_LEFT:
+    case HB_UNICODE_COMBINING_CLASS_ABOVE_LEFT:
+      /* Left align. */
+      pos.x_offset += base_extents.x_bearing - mark_extents.x_bearing;
+      break;
+
+    case HB_UNICODE_COMBINING_CLASS_ATTACHED_ABOVE_RIGHT:
+    case HB_UNICODE_COMBINING_CLASS_BELOW_RIGHT:
+    case HB_UNICODE_COMBINING_CLASS_ABOVE_RIGHT:
+      /* Right align. */
+      pos.x_offset += base_extents.x_bearing + base_extents.width - mark_extents.width - mark_extents.x_bearing;
+      break;
+  }
+
+  /* Y positioning */
+  switch (combining_class)
+  {
+    case HB_UNICODE_COMBINING_CLASS_DOUBLE_BELOW:
+    case HB_UNICODE_COMBINING_CLASS_BELOW_LEFT:
+    case HB_UNICODE_COMBINING_CLASS_BELOW:
+    case HB_UNICODE_COMBINING_CLASS_BELOW_RIGHT:
+      /* Add gap, fall-through. */
+      base_extents.height -= y_gap;
+
+    case HB_UNICODE_COMBINING_CLASS_ATTACHED_BELOW_LEFT:
+    case HB_UNICODE_COMBINING_CLASS_ATTACHED_BELOW:
+      pos.y_offset += base_extents.y_bearing + base_extents.height - mark_extents.y_bearing;
+      base_extents.height += mark_extents.height;
+      break;
+
+    case HB_UNICODE_COMBINING_CLASS_DOUBLE_ABOVE:
+    case HB_UNICODE_COMBINING_CLASS_ABOVE_LEFT:
+    case HB_UNICODE_COMBINING_CLASS_ABOVE:
+    case HB_UNICODE_COMBINING_CLASS_ABOVE_RIGHT:
+      /* Add gap, fall-through. */
+      base_extents.y_bearing += y_gap;
+      base_extents.height -= y_gap;
+
+    case HB_UNICODE_COMBINING_CLASS_ATTACHED_ABOVE:
+    case HB_UNICODE_COMBINING_CLASS_ATTACHED_ABOVE_RIGHT:
+      pos.y_offset += base_extents.y_bearing - (mark_extents.y_bearing + mark_extents.height);
+      base_extents.y_bearing -= mark_extents.height;
+      base_extents.height += mark_extents.height;
+      break;
+  }
+}
+
+static inline void
+position_around_base (const hb_ot_shape_plan_t *plan,
+		      hb_font_t *font,
+		      hb_buffer_t  *buffer,
+		      unsigned int base,
+		      unsigned int end)
+{
+  hb_glyph_extents_t base_extents;
+  if (!hb_font_get_glyph_extents (font,
+				  buffer->info[base].codepoint,
+				  &base_extents))
+  {
+    /* If extents don't work, zero marks and go home. */
+    zero_mark_advances (buffer, base + 1, end);
+    return;
+  }
+  base_extents.x_bearing += buffer->pos[base].x_offset;
+  base_extents.y_bearing += buffer->pos[base].y_offset;
+
+  /* XXX Handle ligature component positioning... */
+  HB_UNUSED bool is_ligature = is_a_ligature (buffer->info[base]);
+
+  hb_position_t x_offset = 0, y_offset = 0;
+  unsigned int last_combining_class = 255;
+  hb_glyph_extents_t cluster_extents;
+  for (unsigned int i = base + 1; i < end; i++)
+    if (_hb_glyph_info_get_general_category (&buffer->info[i]) == HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK)
+    {
+      unsigned int this_combining_class = recategorize_combining_class (_hb_glyph_info_get_modified_combining_class (&buffer->info[i]));
+      if (this_combining_class != last_combining_class)
+        cluster_extents = base_extents;
+
+      position_mark (plan, font, buffer, base_extents, i, this_combining_class);
+
+      buffer->pos[i].x_advance = 0;
+      buffer->pos[i].y_advance = 0;
+      buffer->pos[i].x_offset += x_offset;
+      buffer->pos[i].y_offset += y_offset;
+
+      /* combine cluster extents. */
+
+      last_combining_class = this_combining_class;
+    } else {
+      x_offset -= buffer->pos[i].x_advance;
+      y_offset -= buffer->pos[i].y_advance;
+    }
+
+
+}
+
+static inline void
+position_cluster (const hb_ot_shape_plan_t *plan,
+		  hb_font_t *font,
+		  hb_buffer_t  *buffer,
+		  unsigned int start,
+		  unsigned int end)
+{
+  if (end - start < 2)
+    return;
+
+  /* Find the base glyph */
+  for (unsigned int i = start; i < end; i++)
+    if (is_a_ligature (buffer->info[i]) ||
+        !(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))))
+    {
+      position_around_base (plan, font, buffer, i, end);
+      break;
+    }
+}
+
 void
 _hb_ot_shape_position_fallback (const hb_ot_shape_plan_t *plan,
 				hb_font_t *font,
 				hb_buffer_t  *buffer)
 {
-  hb_zero_mark_advances (buffer);
+  unsigned int start = 0;
+  unsigned int last_cluster = buffer->info[0].cluster;
+  unsigned int count = buffer->len;
+  for (unsigned int i = 1; i < count; i++)
+    if (buffer->info[i].cluster != last_cluster) {
+      position_cluster (plan, font, buffer, start, i);
+      start = i;
+      last_cluster = buffer->info[i].cluster;
+    }
+  position_cluster (plan, font, buffer, start, count);
 }
diff --git a/src/hb-unicode-private.hh b/src/hb-unicode-private.hh
index 7367f0b..b2af7c8 100644
--- a/src/hb-unicode-private.hh
+++ b/src/hb-unicode-private.hh
@@ -195,4 +195,85 @@ extern HB_INTERNAL const hb_unicode_funcs_t _hb_unicode_funcs_nil;
 #endif
 
 
+/* Modified combining marks */
+
+/* Hebrew
+ *
+ * We permute the "fixed-position" classes 10-26 into the order
+ * described in the SBL Hebrew manual:
+ *
+ * http://www.sbl-site.org/Fonts/SBLHebrewUserManual1.5x.pdf
+ *
+ * (as recommended by:
+ *  http://forum.fontlab.com/archive-old-microsoft-volt-group/vista-and-diacritic-ordering-t6751.0.html)
+ *
+ * More details here:
+ * https://bugzilla.mozilla.org/show_bug.cgi?id=662055
+ */
+#define HB_MODIFIED_COMBINING_CLASS_CCC10 22 /* sheva */
+#define HB_MODIFIED_COMBINING_CLASS_CCC11 15 /* hataf segol */
+#define HB_MODIFIED_COMBINING_CLASS_CCC12 16 /* hataf patah */
+#define HB_MODIFIED_COMBINING_CLASS_CCC13 17 /* hataf qamats */
+#define HB_MODIFIED_COMBINING_CLASS_CCC14 23 /* hiriq */
+#define HB_MODIFIED_COMBINING_CLASS_CCC15 18 /* tsere */
+#define HB_MODIFIED_COMBINING_CLASS_CCC16 19 /* segol */
+#define HB_MODIFIED_COMBINING_CLASS_CCC17 20 /* patah */
+#define HB_MODIFIED_COMBINING_CLASS_CCC18 21 /* qamats */
+#define HB_MODIFIED_COMBINING_CLASS_CCC19 14 /* holam */
+#define HB_MODIFIED_COMBINING_CLASS_CCC20 24 /* qubuts */
+#define HB_MODIFIED_COMBINING_CLASS_CCC21 12 /* dagesh */
+#define HB_MODIFIED_COMBINING_CLASS_CCC22 25 /* meteg */
+#define HB_MODIFIED_COMBINING_CLASS_CCC23 13 /* rafe */
+#define HB_MODIFIED_COMBINING_CLASS_CCC24 10 /* shin dot */
+#define HB_MODIFIED_COMBINING_CLASS_CCC25 11 /* sin dot */
+#define HB_MODIFIED_COMBINING_CLASS_CCC26 26 /* point varika */
+
+/*
+ * Arabic
+ *
+ * Modify to move Shadda (ccc=33) before other marks.  See:
+ * http://unicode.org/faq/normalization.html#8
+ * http://unicode.org/faq/normalization.html#9
+ */
+#define HB_MODIFIED_COMBINING_CLASS_CCC27 28 /* fathatan */
+#define HB_MODIFIED_COMBINING_CLASS_CCC28 29 /* dammatan */
+#define HB_MODIFIED_COMBINING_CLASS_CCC29 30 /* kasratan */
+#define HB_MODIFIED_COMBINING_CLASS_CCC30 31 /* fatha */
+#define HB_MODIFIED_COMBINING_CLASS_CCC31 32 /* damma */
+#define HB_MODIFIED_COMBINING_CLASS_CCC32 33 /* kasra */
+#define HB_MODIFIED_COMBINING_CLASS_CCC33 27 /* shadda */
+#define HB_MODIFIED_COMBINING_CLASS_CCC34 34 /* sukun */
+#define HB_MODIFIED_COMBINING_CLASS_CCC35 35 /* superscript alef */
+
+/* Syriac */
+#define HB_MODIFIED_COMBINING_CLASS_CCC36 36 /* superscript alaph */
+
+/* Telugu
+ *
+ * Modify Telugu length marks (ccc=84, ccc=91).
+ * These are the only matras in the main Indic scripts range that have
+ * a non-zero ccc.  That makes them reorder with the Halant that is
+ * ccc=9.  Just zero them, we don't need them in our Indic shaper.
+ */
+#define HB_MODIFIED_COMBINING_CLASS_CCC84 0 /* length mark */
+#define HB_MODIFIED_COMBINING_CLASS_CCC91 0 /* ai length mark */
+
+/* Thai
+ *
+ * Modify U+0E38 and U+0E39 (ccc=104) to be reordered before U+0E3A (ccc=9).
+ * Uniscribe does this too.
+ */
+#define HB_MODIFIED_COMBINING_CLASS_CCC103 3 /* sara u / sara uu */
+#define HB_MODIFIED_COMBINING_CLASS_CCC107 107 /* mai * */
+
+/* Lao */
+#define HB_MODIFIED_COMBINING_CLASS_CCC118 118 /* sign u / sign uu */
+#define HB_MODIFIED_COMBINING_CLASS_CCC122 122 /* mai * */
+
+/* Tibetan */
+#define HB_MODIFIED_COMBINING_CLASS_CCC129 129 /* sign aa */
+#define HB_MODIFIED_COMBINING_CLASS_CCC130 130 /* sign i */
+#define HB_MODIFIED_COMBINING_CLASS_CCC133 132 /* sign u */
+
+
 #endif /* HB_UNICODE_PRIVATE_HH */
diff --git a/src/hb-unicode.cc b/src/hb-unicode.cc
index f4eae0d..30dc7de 100644
--- a/src/hb-unicode.cc
+++ b/src/hb-unicode.cc
@@ -287,6 +287,7 @@ hb_unicode_decompose_compatibility (hb_unicode_funcs_t *ufuncs,
 }
 
 
+/* See hb-unicode-private.hh for details. */
 const uint8_t
 _hb_modified_combining_class[256] =
 {
@@ -298,58 +299,37 @@ _hb_modified_combining_class[256] =
   9, /* HB_UNICODE_COMBINING_CLASS_VIRAMA */
 
   /* Hebrew */
-
-  /*
-   * We permute the "fixed-position" classes 10-26 into the order
-   * described in the SBL Hebrew manual:
-   *
-   * http://www.sbl-site.org/Fonts/SBLHebrewUserManual1.5x.pdf
-   *
-   * (as recommended by:
-   *  http://forum.fontlab.com/archive-old-microsoft-volt-group/vista-and-diacritic-ordering-t6751.0.html)
-   *
-   * More details here:
-   * https://bugzilla.mozilla.org/show_bug.cgi?id=662055
-   */
-  22, /* HB_UNICODE_COMBINING_CLASS_CCC10 sheva */
-  15, /* HB_UNICODE_COMBINING_CLASS_CCC11 hataf segol */
-  16, /* HB_UNICODE_COMBINING_CLASS_CCC12 hataf patah*/
-  17, /* HB_UNICODE_COMBINING_CLASS_CCC13 hataf qamats */
-  23, /* HB_UNICODE_COMBINING_CLASS_CCC14 hiriq */
-  18, /* HB_UNICODE_COMBINING_CLASS_CCC15 tsere */
-  19, /* HB_UNICODE_COMBINING_CLASS_CCC16 segol */
-  20, /* HB_UNICODE_COMBINING_CLASS_CCC17 patah */
-  21, /* HB_UNICODE_COMBINING_CLASS_CCC18 qamats */
-  14, /* HB_UNICODE_COMBINING_CLASS_CCC19 holam */
-  24, /* HB_UNICODE_COMBINING_CLASS_CCC20 qubuts */
-  12, /* HB_UNICODE_COMBINING_CLASS_CCC21 dagesh */
-  25, /* HB_UNICODE_COMBINING_CLASS_CCC22 meteg */
-  13, /* HB_UNICODE_COMBINING_CLASS_CCC23 rafe */
-  10, /* HB_UNICODE_COMBINING_CLASS_CCC24 shin dot */
-  11, /* HB_UNICODE_COMBINING_CLASS_CCC25 sin dot */
-
-  26, /* HB_UNICODE_COMBINING_CLASS_CCC26 */
+  HB_MODIFIED_COMBINING_CLASS_CCC10,
+  HB_MODIFIED_COMBINING_CLASS_CCC11,
+  HB_MODIFIED_COMBINING_CLASS_CCC12,
+  HB_MODIFIED_COMBINING_CLASS_CCC13,
+  HB_MODIFIED_COMBINING_CLASS_CCC14,
+  HB_MODIFIED_COMBINING_CLASS_CCC15,
+  HB_MODIFIED_COMBINING_CLASS_CCC16,
+  HB_MODIFIED_COMBINING_CLASS_CCC17,
+  HB_MODIFIED_COMBINING_CLASS_CCC18,
+  HB_MODIFIED_COMBINING_CLASS_CCC19,
+  HB_MODIFIED_COMBINING_CLASS_CCC20,
+  HB_MODIFIED_COMBINING_CLASS_CCC21,
+  HB_MODIFIED_COMBINING_CLASS_CCC22,
+  HB_MODIFIED_COMBINING_CLASS_CCC23,
+  HB_MODIFIED_COMBINING_CLASS_CCC24,
+  HB_MODIFIED_COMBINING_CLASS_CCC25,
+  HB_MODIFIED_COMBINING_CLASS_CCC26,
 
   /* Arabic */
-
-  /*
-   * Modify to move Shadda (ccc=33) before other marks.  See:
-   * http://unicode.org/faq/normalization.html#8
-   * http://unicode.org/faq/normalization.html#9
-   */
-  28, /* HB_UNICODE_COMBINING_CLASS_CCC27 */
-  29, /* HB_UNICODE_COMBINING_CLASS_CCC28 */
-  30, /* HB_UNICODE_COMBINING_CLASS_CCC29 */
-  31, /* HB_UNICODE_COMBINING_CLASS_CCC30 */
-  32, /* HB_UNICODE_COMBINING_CLASS_CCC31 */
-  33, /* HB_UNICODE_COMBINING_CLASS_CCC32 */
-  27, /* HB_UNICODE_COMBINING_CLASS_CCC33 shadda */
-
-  34, /* HB_UNICODE_COMBINING_CLASS_CCC34 */
-  35, /* HB_UNICODE_COMBINING_CLASS_CCC35 */
+  HB_MODIFIED_COMBINING_CLASS_CCC27,
+  HB_MODIFIED_COMBINING_CLASS_CCC28,
+  HB_MODIFIED_COMBINING_CLASS_CCC29,
+  HB_MODIFIED_COMBINING_CLASS_CCC30,
+  HB_MODIFIED_COMBINING_CLASS_CCC31,
+  HB_MODIFIED_COMBINING_CLASS_CCC32,
+  HB_MODIFIED_COMBINING_CLASS_CCC33,
+  HB_MODIFIED_COMBINING_CLASS_CCC34,
+  HB_MODIFIED_COMBINING_CLASS_CCC35,
 
   /* Syriac */
-  36, /* HB_UNICODE_COMBINING_CLASS_CCC36 */
+  HB_MODIFIED_COMBINING_CLASS_CCC36,
 
   37, 38, 39,
   40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59,
@@ -357,41 +337,28 @@ _hb_modified_combining_class[256] =
   80, 81, 82, 83,
 
   /* Telugu */
-
-  /*
-   * Modify Telugu length marks (ccc=84, ccc=91).
-   * These are the only matras in the main Indic scripts range that have
-   * a non-zero ccc.  That makes them reorder with the Halant that is
-   * ccc=9.  Just zero them, we don't need them in our Indic shaper.
-   */
-  0, /* HB_UNICODE_COMBINING_CLASS_CCC84 */
+  HB_MODIFIED_COMBINING_CLASS_CCC84,
   85, 86, 87, 88, 89, 90,
-  0, /* HB_UNICODE_COMBINING_CLASS_CCC91 */
+  HB_MODIFIED_COMBINING_CLASS_CCC91,
   92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102,
 
   /* Thai */
-
-  /*
-   * Modify U+0E38 and U+0E39 (ccc=104) to be reordered before U+0E3A (ccc=9).
-   * Uniscribe does this too.
-   */
-  3, /* HB_UNICODE_COMBINING_CLASS_CCC103 */
-
+  HB_MODIFIED_COMBINING_CLASS_CCC103,
   104, 105, 106,
-  107, /* HB_UNICODE_COMBINING_CLASS_CCC107 */
+  HB_MODIFIED_COMBINING_CLASS_CCC107,
   108, 109, 110, 111, 112, 113, 114, 115, 116, 117,
 
   /* Lao */
-  118, /* HB_UNICODE_COMBINING_CLASS_CCC118 */
+  HB_MODIFIED_COMBINING_CLASS_CCC118,
   119, 120, 121,
-  122, /* HB_UNICODE_COMBINING_CLASS_CCC122 */
+  HB_MODIFIED_COMBINING_CLASS_CCC122,
   123, 124, 125, 126, 127, 128,
 
   /* Tibetan */
-  129, /* HB_UNICODE_COMBINING_CLASS_CCC129 */
-  130, /* HB_UNICODE_COMBINING_CLASS_CCC130 */
+  HB_MODIFIED_COMBINING_CLASS_CCC129,
+  HB_MODIFIED_COMBINING_CLASS_CCC130,
   131,
-  132, /* HB_UNICODE_COMBINING_CLASS_CCC133 */
+  HB_MODIFIED_COMBINING_CLASS_CCC133,
   133, 134, 135, 136, 137, 138, 139,
 
 
commit fb56e7628362a73e20f7f0b49fe31e802dc01f4f
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Tue Aug 7 23:44:47 2012 -0400

    [hb-old] Fix warnings

diff --git a/src/hb-old/harfbuzz-shaper.cpp b/src/hb-old/harfbuzz-shaper.cpp
index f410780..d1e2335 100644
--- a/src/hb-old/harfbuzz-shaper.cpp
+++ b/src/hb-old/harfbuzz-shaper.cpp
@@ -465,8 +465,10 @@ static void dump_string(HB_Buffer buffer)
 #define DEBUG if (1) ; else printf
 #endif
 
+#if 0
 #define DefaultLangSys 0xffff
 #define DefaultScript HB_MAKE_TAG('D', 'F', 'L', 'T')
+#endif
 
 enum {
     RequiresGsub = 1,
commit affaf8a0e5aa38e5820455f789eebf916e02eb7b
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Tue Aug 7 22:41:38 2012 -0400

    [OT] Start adding fallback positioning
    
    Used when there is no GPOS.

diff --git a/src/Makefile.am b/src/Makefile.am
index 9b583fd..fb7f791 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -87,6 +87,8 @@ HBSOURCES += \
 	hb-ot-shape-complex-private.hh \
 	hb-ot-shape-normalize-private.hh \
 	hb-ot-shape-normalize.cc \
+	hb-ot-shape-position-fallback-private.hh \
+	hb-ot-shape-position-fallback.cc \
 	hb-ot-shape-private.hh \
 	$(NULL)
 HBHEADERS += \
diff --git a/src/hb-ot-shape-position-fallback-private.hh b/src/hb-ot-shape-position-fallback-private.hh
new file mode 100644
index 0000000..f84752e
--- /dev/null
+++ b/src/hb-ot-shape-position-fallback-private.hh
@@ -0,0 +1,39 @@
+/*
+ * Copyright © 2012  Google, Inc.
+ *
+ *  This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Google Author(s): Behdad Esfahbod
+ */
+
+#ifndef HB_OT_POSITION_FALLBACK_PRIVATE_HH
+#define HB_OT_POSITION_FALLBACK_PRIVATE_HH
+
+#include "hb-private.hh"
+
+#include "hb-ot-shape-private.hh"
+
+
+HB_INTERNAL void _hb_ot_shape_position_fallback (const hb_ot_shape_plan_t *plan,
+						 hb_font_t *font,
+						 hb_buffer_t  *buffer);
+
+#endif /* HB_OT_POSITION_FALLBACK_PRIVATE_HH */
diff --git a/src/hb-ot-shape-position-fallback.cc b/src/hb-ot-shape-position-fallback.cc
new file mode 100644
index 0000000..13cebe7
--- /dev/null
+++ b/src/hb-ot-shape-position-fallback.cc
@@ -0,0 +1,47 @@
+/*
+ * Copyright © 2011,2012  Google, Inc.
+ *
+ *  This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Google Author(s): Behdad Esfahbod
+ */
+
+#include "hb-ot-shape-position-fallback-private.hh"
+
+static void
+hb_zero_mark_advances (hb_buffer_t *buffer)
+{
+  unsigned int count = buffer->len;
+  for (unsigned int i = 0; i < count; i++)
+    if (_hb_glyph_info_get_general_category (&buffer->info[i]) == HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK)
+    {
+      buffer->pos[i].x_advance = 0;
+      buffer->pos[i].y_advance = 0;
+    }
+}
+
+void
+_hb_ot_shape_position_fallback (const hb_ot_shape_plan_t *plan,
+				hb_font_t *font,
+				hb_buffer_t  *buffer)
+{
+  hb_zero_mark_advances (buffer);
+}
diff --git a/src/hb-ot-shape.cc b/src/hb-ot-shape.cc
index 4abb76f..6ec17c5 100644
--- a/src/hb-ot-shape.cc
+++ b/src/hb-ot-shape.cc
@@ -32,8 +32,9 @@
 #include "hb-shaper-impl-private.hh"
 
 #include "hb-ot-shape-private.hh"
-#include "hb-ot-shape-normalize-private.hh"
 #include "hb-ot-shape-complex-private.hh"
+#include "hb-ot-shape-normalize-private.hh"
+#include "hb-ot-shape-position-fallback-private.hh"
 
 #include "hb-ot-layout-private.hh"
 #include "hb-set-private.hh"
@@ -207,7 +208,6 @@ _hb_ot_shaper_shape_plan_data_destroy (hb_ot_shaper_shape_plan_data_t *plan)
 
 struct hb_ot_shape_context_t
 {
-  /* Input to hb_ot_shape_internal() */
   hb_ot_shape_plan_t *plan;
   hb_font_t *font;
   hb_face_t *face;
@@ -392,18 +392,6 @@ hb_position_default (hb_ot_shape_context_t *c)
 }
 
 static void
-hb_zero_mark_advances (hb_ot_shape_context_t *c)
-{
-  unsigned int count = c->buffer->len;
-  for (unsigned int i = 0; i < count; i++)
-    if (_hb_glyph_info_get_general_category (&c->buffer->info[i]) == HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK)
-    {
-      c->buffer->pos[i].x_advance = 0;
-      c->buffer->pos[i].y_advance = 0;
-    }
-}
-
-static void
 hb_ot_position_complex (hb_ot_shape_context_t *c)
 {
 
@@ -429,8 +417,7 @@ hb_ot_position_complex (hb_ot_shape_context_t *c)
     }
 
     c->applied_position_complex = true;
-  } else
-    hb_zero_mark_advances (c);
+  }
 
   hb_ot_layout_position_finish (c->font, c->buffer, c->plan->shaper->zero_width_attached_marks);
 
@@ -438,9 +425,9 @@ hb_ot_position_complex (hb_ot_shape_context_t *c)
 }
 
 static void
-hb_position_complex_fallback (hb_ot_shape_context_t *c HB_UNUSED)
+hb_position_complex_fallback (hb_ot_shape_context_t *c)
 {
-  /* TODO Mark pos */
+  _hb_ot_shape_position_fallback (c->plan, c->font, c->buffer);
 }
 
 static void
commit 7e4920fd1577987bf6804f67765e22a84983e057
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Tue Aug 7 22:32:23 2012 -0400

    Minor

diff --git a/src/hb-ot-shape.cc b/src/hb-ot-shape.cc
index 9e1a4c4..4abb76f 100644
--- a/src/hb-ot-shape.cc
+++ b/src/hb-ot-shape.cc
@@ -301,7 +301,7 @@ hb_mirror_chars (hb_ot_shape_context_t *c)
   for (unsigned int i = 0; i < count; i++) {
     hb_codepoint_t codepoint = unicode->mirroring (c->buffer->info[i].codepoint);
     if (likely (codepoint == c->buffer->info[i].codepoint))
-      c->buffer->info[i].mask |= rtlm_mask; /* XXX this should be moved to before setting user-feature masks */
+      c->buffer->info[i].mask |= rtlm_mask; /* TODO this should be moved to before setting user-feature masks */
     else
       c->buffer->info[i].codepoint = codepoint;
   }
commit 472f229a63f0d1bb21b02179ef430b7698df8f12
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Tue Aug 7 22:25:24 2012 -0400

    [GSUB] Generalize would_apply()
    
    Fixes logic also, where before we were always matching if glyphs_len==1
    and a ligature started with the glyph.

diff --git a/src/hb-ot-layout-gsub-table.hh b/src/hb-ot-layout-gsub-table.hh
index b69af4c..1114418 100644
--- a/src/hb-ot-layout-gsub-table.hh
+++ b/src/hb-ot-layout-gsub-table.hh
@@ -473,7 +473,14 @@ struct Ligature
 
   inline bool would_apply (hb_would_apply_context_t *c) const
   {
-    return c->len == 1 || (c->len == 2 && component.len == 2 && component[1] == c->second);
+    if (c->len != component.len)
+      return false;
+
+    for (unsigned int i = 1; i < c->len; i++)
+      if (likely (c->glyphs[i] != component[i]))
+	return false;
+
+    return true;
   }
 
   inline bool apply (hb_apply_context_t *c) const
@@ -706,7 +713,7 @@ struct LigatureSubstFormat1
 
   inline bool would_apply (hb_would_apply_context_t *c) const
   {
-    return (this+ligatureSet[(this+coverage) (c->first)]).would_apply (c);
+    return (this+ligatureSet[(this+coverage) (c->glyphs[0])]).would_apply (c);
   }
 
   inline bool apply (hb_apply_context_t *c) const
@@ -1064,8 +1071,16 @@ struct SubstLookupSubTable
 			   unsigned int lookup_type) const
   {
     TRACE_WOULD_APPLY ();
-    if (get_coverage (lookup_type).get_coverage (c->first) == NOT_COVERED) return false;
-    if (c->len == 1) return true; /* Done! */
+    if (get_coverage (lookup_type).get_coverage (c->glyphs[0]) == NOT_COVERED) return false;
+    if (c->len == 1) {
+      switch (lookup_type) {
+      case Single:
+      case Multiple:
+      case Alternate:
+      case ReverseChainSingle:
+        return true;
+      }
+    }
 
     /* Only need to look further for lookups that support substitutions
      * of input longer than 1. */
@@ -1170,7 +1185,8 @@ struct SubstLookup : Lookup
 
   inline bool would_apply (hb_would_apply_context_t *c) const
   {
-    if (!c->digest.may_have (c->first)) return false;
+    if (unlikely (!c->len)) return false;
+    if (!c->digest.may_have (c->glyphs[0])) return false;
     unsigned int lookup_type = get_type ();
     unsigned int count = get_subtable_count ();
     for (unsigned int i = 0; i < count; i++)
diff --git a/src/hb-ot-layout-gsubgpos-private.hh b/src/hb-ot-layout-gsubgpos-private.hh
index d91686d..fd5fb19 100644
--- a/src/hb-ot-layout-gsubgpos-private.hh
+++ b/src/hb-ot-layout-gsubgpos-private.hh
@@ -43,15 +43,6 @@
 	hb_auto_trace_t<HB_DEBUG_CLOSURE> trace (&c->debug_depth, "CLOSURE", this, HB_FUNC, "");
 
 
-/* TODO Add TRACE_RETURN annotation to gsub. */
-#ifndef HB_DEBUG_WOULD_APPLY
-#define HB_DEBUG_WOULD_APPLY (HB_DEBUG+0)
-#endif
-
-#define TRACE_WOULD_APPLY() \
-	hb_auto_trace_t<HB_DEBUG_WOULD_APPLY> trace (&c->debug_depth, "WOULD_APPLY", this, HB_FUNC, "first %u second %u", c->first, c->second);
-
-
 struct hb_closure_context_t
 {
   hb_face_t *face;
@@ -71,23 +62,31 @@ struct hb_closure_context_t
 
 
 
+/* TODO Add TRACE_RETURN annotation to gsub. */
+#ifndef HB_DEBUG_WOULD_APPLY
+#define HB_DEBUG_WOULD_APPLY (HB_DEBUG+0)
+#endif
+
+#define TRACE_WOULD_APPLY() \
+	hb_auto_trace_t<HB_DEBUG_WOULD_APPLY> trace (&c->debug_depth, "WOULD_APPLY", this, HB_FUNC, "%d glyphs", c->len);
+
 
 struct hb_would_apply_context_t
 {
   hb_face_t *face;
-  hb_codepoint_t first;
-  hb_codepoint_t second;
+  const hb_codepoint_t *glyphs;
   unsigned int len;
   const hb_set_digest_t digest;
   unsigned int debug_depth;
 
   hb_would_apply_context_t (hb_face_t *face_,
-			    hb_codepoint_t first_,
-			    hb_codepoint_t second_,
+			    const hb_codepoint_t *glyphs_,
+			    unsigned int len_,
 			    const hb_set_digest_t *digest_
 			    ) :
 			      face (face_),
-			      first (first_), second (second_), len (second == (hb_codepoint_t) -1 ? 1 : 2),
+			      glyphs (glyphs_),
+			      len (len_),
 			      digest (*digest_),
 			      debug_depth (0) {};
 };
@@ -407,7 +406,7 @@ static inline bool would_match_input (hb_would_apply_context_t *c,
     return false;
 
   for (unsigned int i = 1; i < count; i++)
-    if (likely (!match_func (c->second, input[i - 1], match_data)))
+    if (likely (!match_func (c->glyphs[i], input[i - 1], match_data)))
       return false;
 
   return true;
@@ -757,7 +756,7 @@ struct ContextFormat1
   {
     TRACE_WOULD_APPLY ();
 
-    const RuleSet &rule_set = this+ruleSet[(this+coverage) (c->first)];
+    const RuleSet &rule_set = this+ruleSet[(this+coverage) (c->glyphs[0])];
     struct ContextApplyLookupContext lookup_context = {
       {match_glyph, NULL},
       NULL
@@ -830,7 +829,7 @@ struct ContextFormat2
     TRACE_WOULD_APPLY ();
 
     const ClassDef &class_def = this+classDef;
-    unsigned int index = class_def (c->first);
+    unsigned int index = class_def (c->glyphs[0]);
     const RuleSet &rule_set = this+ruleSet[index];
     struct ContextApplyLookupContext lookup_context = {
       {match_class, NULL},
@@ -1253,7 +1252,7 @@ struct ChainContextFormat1
   {
     TRACE_WOULD_APPLY ();
 
-    const ChainRuleSet &rule_set = this+ruleSet[(this+coverage) (c->first)];
+    const ChainRuleSet &rule_set = this+ruleSet[(this+coverage) (c->glyphs[0])];
     struct ChainContextApplyLookupContext lookup_context = {
       {match_glyph, NULL},
       {NULL, NULL, NULL}
@@ -1329,7 +1328,7 @@ struct ChainContextFormat2
 
     const ClassDef &input_class_def = this+inputClassDef;
 
-    unsigned int index = input_class_def (c->first);
+    unsigned int index = input_class_def (c->glyphs[0]);
     const ChainRuleSet &rule_set = this+ruleSet[index];
     struct ChainContextApplyLookupContext lookup_context = {
       {match_class, NULL},
diff --git a/src/hb-ot-layout.cc b/src/hb-ot-layout.cc
index 0280f0b..91deaa2 100644
--- a/src/hb-ot-layout.cc
+++ b/src/hb-ot-layout.cc
@@ -415,9 +415,8 @@ hb_ot_layout_would_substitute_lookup_fast (hb_face_t            *face,
 					   unsigned int          glyphs_length,
 					   unsigned int          lookup_index)
 {
-  if (unlikely (glyphs_length < 1 || glyphs_length > 2)) return false;
   if (unlikely (lookup_index >= hb_ot_layout_from_face (face)->gsub_lookup_count)) return false;
-  hb_would_apply_context_t c (face, glyphs[0], glyphs_length == 2 ? glyphs[1] : -1, &hb_ot_layout_from_face (face)->gsub_digests[lookup_index]);
+  hb_would_apply_context_t c (face, glyphs, glyphs_length, &hb_ot_layout_from_face (face)->gsub_digests[lookup_index]);
   return hb_ot_layout_from_face (face)->gsub->would_substitute_lookup (&c, lookup_index);
 }
 
commit 6f3a300138f659020c21c3e08b7981c78df5f332
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Tue Aug 7 22:13:25 2012 -0400

    Add hb_font_glyph_from/to_string

diff --git a/src/hb-font-private.hh b/src/hb-font-private.hh
index 56c5913..6bbf8d8 100644
--- a/src/hb-font-private.hh
+++ b/src/hb-font-private.hh
@@ -254,6 +254,7 @@ struct hb_font_t {
   inline hb_bool_t get_glyph_name (hb_codepoint_t glyph,
 				   char *name, unsigned int size)
   {
+    if (size) *name = '\0';
     return klass->get.glyph_name (this, user_data,
 				  glyph,
 				  name, size,
@@ -263,6 +264,8 @@ struct hb_font_t {
   inline hb_bool_t get_glyph_from_name (const char *name, int len, /* -1 means nul-terminated */
 					hb_codepoint_t *glyph)
   {
+    *glyph = 0;
+    if (len == -1) len = strlen (name);
     return klass->get.glyph_from_name (this, user_data,
 				       name, len,
 				       glyph,
@@ -377,6 +380,46 @@ struct hb_font_t {
     return ret;
   }
 
+  /* Generates gidDDD if glyph has no name. */
+  inline void
+  glyph_to_string (hb_codepoint_t glyph,
+		   char *s, unsigned int size)
+  {
+    if (get_glyph_name (glyph, s, size)) return;
+
+    snprintf (s, size, "gid%u", glyph);
+  }
+
+  /* Parses gidDDD and uniUUUU strings automatically. */
+  inline hb_bool_t
+  glyph_from_string (const char *s, int len, /* -1 means nul-terminated */
+		     hb_codepoint_t *glyph)
+  {
+    if (get_glyph_from_name (s, len, glyph)) return true;
+
+    if (len == -1) len = strlen (s);
+
+    /* Straight glyph index. */
+    if (hb_codepoint_parse (s, len, 10, glyph))
+      return true;
+
+    if (len > 3)
+    {
+      /* gidDDD syntax for glyph indices. */
+      if (0 == strncmp (s, "gid", 3) &&
+	  hb_codepoint_parse (s + 3, len - 3, 10, glyph))
+	return true;
+
+      /* uniUUUU and other Unicode character indices. */
+      hb_codepoint_t unichar;
+      if (0 == strncmp (s, "uni", 3) &&
+	  hb_codepoint_parse (s + 3, len - 3, 16, &unichar) &&
+	  get_glyph (unichar, 0, glyph))
+	return true;
+    }
+
+    return false;
+  }
 
   private:
   inline hb_position_t em_scale (int16_t v, int scale) { return v * (int64_t) scale / hb_face_get_upem (this->face); }
diff --git a/src/hb-font.cc b/src/hb-font.cc
index 409b9e8..e5e4af7 100644
--- a/src/hb-font.cc
+++ b/src/hb-font.cc
@@ -200,6 +200,7 @@ hb_font_get_glyph_name_nil (hb_font_t *font,
   if (font->parent)
     return hb_font_get_glyph_name (font->parent, glyph, name, size);
 
+  if (size) *name = '\0';
   return false;
 }
 
@@ -410,10 +411,7 @@ hb_font_get_glyph_name (hb_font_t *font,
 			hb_codepoint_t glyph,
 			char *name, unsigned int size)
 {
-  hb_bool_t ret = font->get_glyph_name (glyph, name, size);
-  if (!ret)
-    snprintf (name, size, "gid%u", glyph);
-  return ret;
+  return font->get_glyph_name (glyph, name, size);
 }
 
 hb_bool_t
@@ -490,6 +488,24 @@ hb_font_get_glyph_contour_point_for_origin (hb_font_t *font,
   return font->get_glyph_contour_point_for_origin (glyph, point_index, direction, x, y);
 }
 
+/* Generates gidDDD if glyph has no name. */
+void
+hb_font_glyph_to_string (hb_font_t *font,
+			 hb_codepoint_t glyph,
+			 char *s, unsigned int size)
+{
+  font->glyph_to_string (glyph, s, size);
+}
+
+/* Parses gidDDD and uniUUUU strings automatically. */
+hb_bool_t
+hb_font_glyph_from_string (hb_font_t *font,
+			   const char *s, int len, /* -1 means nul-terminated */
+			   hb_codepoint_t *glyph)
+{
+  return font->glyph_from_string (s, len, glyph);
+}
+
 
 /*
  * hb_face_t
diff --git a/src/hb-font.h b/src/hb-font.h
index ae4fc9b..0fde258 100644
--- a/src/hb-font.h
+++ b/src/hb-font.h
@@ -346,6 +346,17 @@ hb_font_get_glyph_contour_point_for_origin (hb_font_t *font,
 					    hb_direction_t direction,
 					    hb_position_t *x, hb_position_t *y);
 
+/* Generates gidDDD if glyph has no name. */
+void
+hb_font_glyph_to_string (hb_font_t *font,
+			 hb_codepoint_t glyph,
+			 char *s, unsigned int size);
+/* Parses gidDDD and uniUUUU strings automatically. */
+hb_bool_t
+hb_font_glyph_from_string (hb_font_t *font,
+			   const char *s, int len, /* -1 means nul-terminated */
+			   hb_codepoint_t *glyph);
+
 
 /*
  * hb_font_t
diff --git a/src/hb-private.hh b/src/hb-private.hh
index 0f1e5ce..8655020 100644
--- a/src/hb-private.hh
+++ b/src/hb-private.hh
@@ -791,6 +791,22 @@ hb_bubble_sort (T *array, unsigned int len, int(*compar)(const T *, const T *))
   hb_bubble_sort (array, len, compar, (int *) NULL);
 }
 
+static inline hb_bool_t
+hb_codepoint_parse (const char *s, unsigned int len, int base, hb_codepoint_t *out)
+{
+  /* Pain because we don't know whether s is nul-terminated. */
+  char buf[64];
+  strncpy (buf, s, MIN (ARRAY_LENGTH (buf) - 1, len));
+  buf[MIN (ARRAY_LENGTH (buf) - 1, len)] = '\0';
+
+  char *end;
+  errno = 0;
+  unsigned long v = strtoul (buf, &end, base);
+  if (errno) return false;
+  if (*end) return false;
+  *out = v;
+  return true;
+}
 
 
 #endif /* HB_PRIVATE_HH */
diff --git a/src/test-would-substitute.cc b/src/test-would-substitute.cc
index bf64a29..95626ed 100644
--- a/src/test-would-substitute.cc
+++ b/src/test-would-substitute.cc
@@ -88,7 +88,16 @@ main (int argc, char **argv)
   hb_blob_destroy (blob);
   blob = NULL;
 
+  hb_font_t *font = hb_font_create (face);
+#ifdef HAVE_FREETYPE
+  hb_ft_font_set_funcs (font);
+#endif
+
   unsigned int len = argc - 3;
-  hb_codepoint_t glyphs[2] = {strtol (argv[3], NULL, 0), argc > 4 ? strtol (argv[4], NULL, 0) : (hb_codepoint_t) -1};
+  hb_codepoint_t glyphs[2];
+  if (!hb_font_glyph_from_string (font, argv[3], -1, &glyphs[0]) ||
+      (argc > 4 &&
+       !hb_font_glyph_from_string (font, argv[4], -1, &glyphs[1])))
+    return 2;
   return !hb_ot_layout_would_substitute_lookup (face, glyphs, len, strtol (argv[2], NULL, 0));
 }
diff --git a/util/options.cc b/util/options.cc
index d2a3846..c05cee6 100644
--- a/util/options.cc
+++ b/util/options.cc
@@ -749,7 +749,7 @@ format_options_t::serialize_glyphs (hb_buffer_t *buffer,
 
     char glyph_name[128];
     if (show_glyph_names) {
-      hb_font_get_glyph_name (font, info->codepoint, glyph_name, sizeof (glyph_name));
+      hb_font_glyph_to_string (font, info->codepoint, glyph_name, sizeof (glyph_name));
       g_string_append_printf (gs, "%s", glyph_name);
     } else
       g_string_append_printf (gs, "%u", info->codepoint);
commit eb56f6ae96260c5b4bcd4e1dfb7ab733a230f3a8
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Tue Aug 7 21:44:25 2012 -0400

    Minor

diff --git a/src/hb-mutex-private.hh b/src/hb-mutex-private.hh
index 8efd27e..fc8ef49 100644
--- a/src/hb-mutex-private.hh
+++ b/src/hb-mutex-private.hh
@@ -44,6 +44,7 @@
 
 #elif !defined(HB_NO_MT) && defined(_MSC_VER) || defined(__MINGW32__)
 
+#define WIN32_LEAN_AND_MEAN
 #include <windows.h>
 typedef CRITICAL_SECTION hb_mutex_impl_t;
 #define HB_MUTEX_IMPL_INIT	{ NULL, 0, 0, NULL, NULL, 0 }
diff --git a/src/hb-uniscribe.cc b/src/hb-uniscribe.cc
index 0ca87ec..79e62a0 100644
--- a/src/hb-uniscribe.cc
+++ b/src/hb-uniscribe.cc
@@ -25,6 +25,7 @@
  */
 
 #define _WIN32_WINNT 0x0600
+#define WIN32_LEAN_AND_MEAN
 
 #define HB_SHAPER uniscribe
 #include "hb-shaper-impl-private.hh"
diff --git a/src/hb-uniscribe.h b/src/hb-uniscribe.h
index bb99f39..51887c8 100644
--- a/src/hb-uniscribe.h
+++ b/src/hb-uniscribe.h
@@ -29,7 +29,9 @@
 
 #include "hb.h"
 
+#ifndef _WIN32_WINNT
 #define _WIN32_WINNT 0x0600
+#endif
 #include <windows.h>
 
 HB_BEGIN_DECLS
commit f4e48adcdd4315ce09e755f87a0f801d88194f42
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Tue Aug 7 21:12:49 2012 -0400

    [OT] Apply 'rclt' feature in horizontal mode
    
    'rclt' is "Required Contextual Forms" being proposed by Microsoft.
    It's like 'calt', but supposedly always on.  We apply 'calt' anyway,
    and now apply this too.

diff --git a/src/hb-ot-shape.cc b/src/hb-ot-shape.cc
index 08457be..9e1a4c4 100644
--- a/src/hb-ot-shape.cc
+++ b/src/hb-ot-shape.cc
@@ -54,6 +54,7 @@ hb_tag_t horizontal_features[] = {
   HB_TAG('c','l','i','g'),
   HB_TAG('c','u','r','s'),
   HB_TAG('k','e','r','n'),
+  HB_TAG('r','c','l','t'),
 };
 
 /* Note:
commit b1914b8bd08ecdea79930dda7e3bb2ae9e6134a1
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Tue Aug 7 16:57:48 2012 -0400

    Fix warnings

diff --git a/src/hb-icu.cc b/src/hb-icu.cc
index d7fd185..5e92058 100644
--- a/src/hb-icu.cc
+++ b/src/hb-icu.cc
@@ -175,7 +175,7 @@ hb_icu_unicode_compose (hb_unicode_funcs_t *ufuncs HB_UNUSED,
     return false;
 
   UChar utf16[4], normalized[5];
-  int len;
+  unsigned int len;
   hb_bool_t ret, err;
   UErrorCode icu_err;
 
@@ -208,7 +208,7 @@ hb_icu_unicode_decompose (hb_unicode_funcs_t *ufuncs HB_UNUSED,
 			  void               *user_data HB_UNUSED)
 {
   UChar utf16[2], normalized[2 * HB_UNICODE_MAX_DECOMPOSITION_LEN + 1];
-  int len;
+  unsigned int len;
   hb_bool_t ret, err;
   UErrorCode icu_err;
 
@@ -278,7 +278,7 @@ hb_icu_unicode_decompose_compatibility (hb_unicode_funcs_t *ufuncs HB_UNUSED,
 					void               *user_data HB_UNUSED)
 {
   UChar utf16[2], normalized[2 * HB_UNICODE_MAX_DECOMPOSITION_LEN + 1];
-  int len;
+  unsigned int len;
   int32_t utf32_len;
   hb_bool_t err;
   UErrorCode icu_err;
commit 0f8881d6bbf6cd59938315eeff9b71cfc736aa4e
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Tue Aug 7 16:57:02 2012 -0400

    More refactoring

diff --git a/src/hb-ot-shape-normalize.cc b/src/hb-ot-shape-normalize.cc
index 53f575f..b5e0b4d 100644
--- a/src/hb-ot-shape-normalize.cc
+++ b/src/hb-ot-shape-normalize.cc
@@ -87,6 +87,39 @@ decompose_func (hb_unicode_funcs_t *unicode,
 		hb_codepoint_t *a,
 		hb_codepoint_t *b)
 {
+  /* XXX FIXME, move these to complex shapers and propagage to normalizer.*/
+  switch (ab) {
+    case 0x0AC9  : return false;
+
+    case 0x0931  : return false;
+    case 0x0B94  : return false;
+
+    /* These ones have Unicode decompositions, but we do it
+     * this way to be close to what Uniscribe does. */
+    case 0x0DDA  : *a = 0x0DD9; *b= 0x0DDA; return true;
+    case 0x0DDC  : *a = 0x0DD9; *b= 0x0DDC; return true;
+    case 0x0DDD  : *a = 0x0DD9; *b= 0x0DDD; return true;
+    case 0x0DDE  : *a = 0x0DD9; *b= 0x0DDE; return true;
+
+    case 0x0F77  : *a = 0x0FB2; *b= 0x0F81; return true;
+    case 0x0F79  : *a = 0x0FB3; *b= 0x0F81; return true;
+    case 0x17BE  : *a = 0x17C1; *b= 0x17BE; return true;
+    case 0x17BF  : *a = 0x17C1; *b= 0x17BF; return true;
+    case 0x17C0  : *a = 0x17C1; *b= 0x17C0; return true;
+    case 0x17C4  : *a = 0x17C1; *b= 0x17C4; return true;
+    case 0x17C5  : *a = 0x17C1; *b= 0x17C5; return true;
+    case 0x1925  : *a = 0x1920; *b= 0x1923; return true;
+    case 0x1926  : *a = 0x1920; *b= 0x1924; return true;
+    case 0x1B3C  : *a = 0x1B42; *b= 0x1B3C; return true;
+    case 0x1112E  : *a = 0x11127; *b= 0x11131; return true;
+    case 0x1112F  : *a = 0x11127; *b= 0x11132; return true;
+#if 0
+    case 0x0B57  : *a = 0xno decomp, -> RIGHT; return true;
+    case 0x1C29  : *a = 0xno decomp, -> LEFT; return true;
+    case 0xA9C0  : *a = 0xno decomp, -> RIGHT; return true;
+    case 0x111BF  : *a = 0xno decomp, -> ABOVE; return true;
+#endif
+  }
   return unicode->decompose (ab, a, b);
 }
 
@@ -96,7 +129,133 @@ compose_func (hb_unicode_funcs_t *unicode,
 	      hb_codepoint_t  b,
 	      hb_codepoint_t *ab)
 {
-  return unicode->compose (a, b, ab);
+  /* XXX, this belongs to indic normalizer. */
+  if ((FLAG (unicode->general_category (a)) &
+       (FLAG (HB_UNICODE_GENERAL_CATEGORY_SPACING_MARK) |
+	FLAG (HB_UNICODE_GENERAL_CATEGORY_ENCLOSING_MARK) |
+	FLAG (HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK))))
+    return false;
+  /* XXX, add composition-exclusion exceptions to Indic shaper. */
+  if (a == 0x09AF && b == 0x09BC) { *ab = 0x09DF; return true; }
+
+  /* XXX, these belong to the hebew / default shaper. */
+  /* Hebrew presentation-form shaping.
+   * https://bugzilla.mozilla.org/show_bug.cgi?id=728866 */
+  // Hebrew presentation forms with dagesh, for characters 0x05D0..0x05EA;
+  // note that some letters do not have a dagesh presForm encoded
+  static const hb_codepoint_t sDageshForms[0x05EA - 0x05D0 + 1] = {
+    0xFB30, // ALEF
+    0xFB31, // BET
+    0xFB32, // GIMEL
+    0xFB33, // DALET
+    0xFB34, // HE
+    0xFB35, // VAV
+    0xFB36, // ZAYIN
+    0, // HET
+    0xFB38, // TET
+    0xFB39, // YOD
+    0xFB3A, // FINAL KAF
+    0xFB3B, // KAF
+    0xFB3C, // LAMED
+    0, // FINAL MEM
+    0xFB3E, // MEM
+    0, // FINAL NUN
+    0xFB40, // NUN
+    0xFB41, // SAMEKH
+    0, // AYIN
+    0xFB43, // FINAL PE
+    0xFB44, // PE
+    0, // FINAL TSADI
+    0xFB46, // TSADI
+    0xFB47, // QOF
+    0xFB48, // RESH
+    0xFB49, // SHIN
+    0xFB4A // TAV
+  };
+
+  hb_bool_t found = unicode->compose (a, b, ab);
+
+  if (!found && (b & ~0x7F) == 0x0580) {
+      // special-case Hebrew presentation forms that are excluded from
+      // standard normalization, but wanted for old fonts
+      switch (b) {
+      case 0x05B4: // HIRIQ
+	  if (a == 0x05D9) { // YOD
+	      *ab = 0xFB1D;
+	      found = true;
+	  }
+	  break;
+      case 0x05B7: // patah
+	  if (a == 0x05F2) { // YIDDISH YOD YOD
+	      *ab = 0xFB1F;
+	      found = true;
+	  } else if (a == 0x05D0) { // ALEF
+	      *ab = 0xFB2E;
+	      found = true;
+	  }
+	  break;
+      case 0x05B8: // QAMATS
+	  if (a == 0x05D0) { // ALEF
+	      *ab = 0xFB2F;
+	      found = true;
+	  }
+	  break;
+      case 0x05B9: // HOLAM
+	  if (a == 0x05D5) { // VAV
+	      *ab = 0xFB4B;
+	      found = true;
+	  }
+	  break;
+      case 0x05BC: // DAGESH
+	  if (a >= 0x05D0 && a <= 0x05EA) {
+	      *ab = sDageshForms[a - 0x05D0];
+	      found = (*ab != 0);
+	  } else if (a == 0xFB2A) { // SHIN WITH SHIN DOT
+	      *ab = 0xFB2C;
+	      found = true;
+	  } else if (a == 0xFB2B) { // SHIN WITH SIN DOT
+	      *ab = 0xFB2D;
+	      found = true;
+	  }
+	  break;
+      case 0x05BF: // RAFE
+	  switch (a) {
+	  case 0x05D1: // BET
+	      *ab = 0xFB4C;
+	      found = true;
+	      break;
+	  case 0x05DB: // KAF
+	      *ab = 0xFB4D;
+	      found = true;
+	      break;
+	  case 0x05E4: // PE
+	      *ab = 0xFB4E;
+	      found = true;
+	      break;
+	  }
+	  break;
+      case 0x05C1: // SHIN DOT
+	  if (a == 0x05E9) { // SHIN
+	      *ab = 0xFB2A;
+	      found = true;
+	  } else if (a == 0xFB49) { // SHIN WITH DAGESH
+	      *ab = 0xFB2C;
+	      found = true;
+	  }
+	  break;
+      case 0x05C2: // SIN DOT
+	  if (a == 0x05E9) { // SHIN
+	      *ab = 0xFB2B;
+	      found = true;
+	  } else if (a == 0xFB49) { // SHIN WITH DAGESH
+	      *ab = 0xFB2D;
+	      found = true;
+	  }
+	  break;
+      }
+  }
+
+  return found;
 }
 
 static void
diff --git a/src/hb-unicode-private.hh b/src/hb-unicode-private.hh
index 2a67f0a..7367f0b 100644
--- a/src/hb-unicode-private.hh
+++ b/src/hb-unicode-private.hh
@@ -80,173 +80,13 @@ HB_UNICODE_FUNCS_IMPLEMENT_CALLBACKS_SIMPLE
 			    hb_codepoint_t *ab)
   {
     *ab = 0;
-
-    /* XXX, this belongs to indic normalizer. */
-    if ((FLAG (general_category (a)) &
-	 (FLAG (HB_UNICODE_GENERAL_CATEGORY_SPACING_MARK) |
-	  FLAG (HB_UNICODE_GENERAL_CATEGORY_ENCLOSING_MARK) |
-	  FLAG (HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK))))
-      return false;
-    /* XXX, add composition-exclusion exceptions to Indic shaper. */
-    if (a == 0x09AF && b == 0x09BC) { *ab = 0x09DF; return true; }
-
-    /* XXX, these belong to the hebew / default shaper. */
-    /* Hebrew presentation-form shaping.
-     * https://bugzilla.mozilla.org/show_bug.cgi?id=728866 */
-    // Hebrew presentation forms with dagesh, for characters 0x05D0..0x05EA;
-    // note that some letters do not have a dagesh presForm encoded
-    static const hb_codepoint_t sDageshForms[0x05EA - 0x05D0 + 1] = {
-      0xFB30, // ALEF
-      0xFB31, // BET
-      0xFB32, // GIMEL
-      0xFB33, // DALET
-      0xFB34, // HE
-      0xFB35, // VAV
-      0xFB36, // ZAYIN
-      0, // HET
-      0xFB38, // TET
-      0xFB39, // YOD
-      0xFB3A, // FINAL KAF
-      0xFB3B, // KAF
-      0xFB3C, // LAMED
-      0, // FINAL MEM
-      0xFB3E, // MEM
-      0, // FINAL NUN
-      0xFB40, // NUN
-      0xFB41, // SAMEKH
-      0, // AYIN
-      0xFB43, // FINAL PE
-      0xFB44, // PE
-      0, // FINAL TSADI
-      0xFB46, // TSADI
-      0xFB47, // QOF
-      0xFB48, // RESH
-      0xFB49, // SHIN
-      0xFB4A // TAV
-    };
-
-    hb_bool_t found = func.compose (this, a, b, ab, user_data.compose);
-
-    if (!found && (b & ~0x7F) == 0x0580) {
-	// special-case Hebrew presentation forms that are excluded from
-	// standard normalization, but wanted for old fonts
-	switch (b) {
-	case 0x05B4: // HIRIQ
-	    if (a == 0x05D9) { // YOD
-		*ab = 0xFB1D;
-		found = true;
-	    }
-	    break;
-	case 0x05B7: // patah
-	    if (a == 0x05F2) { // YIDDISH YOD YOD
-		*ab = 0xFB1F;
-		found = true;
-	    } else if (a == 0x05D0) { // ALEF
-		*ab = 0xFB2E;
-		found = true;
-	    }
-	    break;
-	case 0x05B8: // QAMATS
-	    if (a == 0x05D0) { // ALEF
-		*ab = 0xFB2F;
-		found = true;
-	    }
-	    break;
-	case 0x05B9: // HOLAM
-	    if (a == 0x05D5) { // VAV
-		*ab = 0xFB4B;
-		found = true;
-	    }
-	    break;
-	case 0x05BC: // DAGESH
-	    if (a >= 0x05D0 && a <= 0x05EA) {
-		*ab = sDageshForms[a - 0x05D0];
-		found = (*ab != 0);
-	    } else if (a == 0xFB2A) { // SHIN WITH SHIN DOT
-		*ab = 0xFB2C;
-		found = true;
-	    } else if (a == 0xFB2B) { // SHIN WITH SIN DOT
-		*ab = 0xFB2D;
-		found = true;
-	    }
-	    break;
-	case 0x05BF: // RAFE
-	    switch (a) {
-	    case 0x05D1: // BET
-		*ab = 0xFB4C;
-		found = true;
-		break;
-	    case 0x05DB: // KAF
-		*ab = 0xFB4D;
-		found = true;
-		break;
-	    case 0x05E4: // PE
-		*ab = 0xFB4E;
-		found = true;
-		break;
-	    }
-	    break;
-	case 0x05C1: // SHIN DOT
-	    if (a == 0x05E9) { // SHIN
-		*ab = 0xFB2A;
-		found = true;
-	    } else if (a == 0xFB49) { // SHIN WITH DAGESH
-		*ab = 0xFB2C;
-		found = true;
-	    }
-	    break;
-	case 0x05C2: // SIN DOT
-	    if (a == 0x05E9) { // SHIN
-		*ab = 0xFB2B;
-		found = true;
-	    } else if (a == 0xFB49) { // SHIN WITH DAGESH
-		*ab = 0xFB2D;
-		found = true;
-	    }
-	    break;
-	}
-    }
-
-    return found;
+    return func.compose (this, a, b, ab, user_data.compose);
   }
 
   inline hb_bool_t decompose (hb_codepoint_t ab,
 			      hb_codepoint_t *a, hb_codepoint_t *b)
   {
-    /* XXX FIXME, move these to complex shapers and propagage to normalizer.*/
-    switch (ab) {
-      case 0x0AC9  : return false;
-
-      case 0x0931  : return false;
-      case 0x0B94  : return false;
-
-      /* These ones have Unicode decompositions, but we do it
-       * this way to be close to what Uniscribe does. */
-      case 0x0DDA  : *a = 0x0DD9; *b= 0x0DDA; return true;
-      case 0x0DDC  : *a = 0x0DD9; *b= 0x0DDC; return true;
-      case 0x0DDD  : *a = 0x0DD9; *b= 0x0DDD; return true;
-      case 0x0DDE  : *a = 0x0DD9; *b= 0x0DDE; return true;
-
-      case 0x0F77  : *a = 0x0FB2; *b= 0x0F81; return true;
-      case 0x0F79  : *a = 0x0FB3; *b= 0x0F81; return true;
-      case 0x17BE  : *a = 0x17C1; *b= 0x17BE; return true;
-      case 0x17BF  : *a = 0x17C1; *b= 0x17BF; return true;
-      case 0x17C0  : *a = 0x17C1; *b= 0x17C0; return true;
-      case 0x17C4  : *a = 0x17C1; *b= 0x17C4; return true;
-      case 0x17C5  : *a = 0x17C1; *b= 0x17C5; return true;
-      case 0x1925  : *a = 0x1920; *b= 0x1923; return true;
-      case 0x1926  : *a = 0x1920; *b= 0x1924; return true;
-      case 0x1B3C  : *a = 0x1B42; *b= 0x1B3C; return true;
-      case 0x1112E  : *a = 0x11127; *b= 0x11131; return true;
-      case 0x1112F  : *a = 0x11127; *b= 0x11132; return true;
-#if 0
-      case 0x0B57  : *a = 0xno decomp, -> RIGHT; return true;
-      case 0x1C29  : *a = 0xno decomp, -> LEFT; return true;
-      case 0xA9C0  : *a = 0xno decomp, -> RIGHT; return true;
-      case 0x111BF  : *a = 0xno decomp, -> ABOVE; return true;
-#endif
-    }
-    *a = ab; *b = 0;
+    *a = *b = 0;
     return func.decompose (this, ab, a, b, user_data.decompose);
   }
 
commit 428dfcab6634ff264570a0a5d715efb8048c3db5
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Tue Aug 7 16:51:48 2012 -0400

    Minor refactoring

diff --git a/src/hb-ot-shape-normalize.cc b/src/hb-ot-shape-normalize.cc
index bc1df54..53f575f 100644
--- a/src/hb-ot-shape-normalize.cc
+++ b/src/hb-ot-shape-normalize.cc
@@ -81,6 +81,24 @@
  *     egrep  "`echo -n ';('; grep ';<' UnicodeData.txt | cut -d';' -f1 | tr '\n' '|'; echo ') '`" UnicodeData.txt
  */
 
+static hb_bool_t
+decompose_func (hb_unicode_funcs_t *unicode,
+		hb_codepoint_t  ab,
+		hb_codepoint_t *a,
+		hb_codepoint_t *b)
+{
+  return unicode->decompose (ab, a, b);
+}
+
+static hb_bool_t
+compose_func (hb_unicode_funcs_t *unicode,
+	      hb_codepoint_t  a,
+	      hb_codepoint_t  b,
+	      hb_codepoint_t *ab)
+{
+  return unicode->compose (a, b, ab);
+}
+
 static void
 output_glyph (hb_buffer_t *buffer, hb_codepoint_t glyph)
 {
@@ -95,7 +113,7 @@ decompose (hb_font_t *font, hb_buffer_t *buffer,
 {
   hb_codepoint_t a, b, glyph;
 
-  if (!buffer->unicode->decompose (ab, &a, &b) ||
+  if (!decompose_func (buffer->unicode, ab, &a, &b) ||
       (b && !font->get_glyph (b, 0, &glyph)))
     return false;
 
@@ -281,9 +299,10 @@ _hb_ot_shape_normalize (hb_font_t *font, hb_buffer_t *buffer,
 	(starter == buffer->out_len - 1 ||
 	 _hb_glyph_info_get_modified_combining_class (&buffer->prev()) < _hb_glyph_info_get_modified_combining_class (&buffer->cur())) &&
 	/* And compose. */
-	buffer->unicode->compose (buffer->out_info[starter].codepoint,
-				  buffer->cur().codepoint,
-				  &composed) &&
+	compose_func (buffer->unicode,
+		      buffer->out_info[starter].codepoint,
+		      buffer->cur().codepoint,
+		      &composed) &&
 	/* And the font has glyph for the composite. */
 	font->get_glyph (composed, 0, &glyph))
     {
commit 61f41849af6ff9edf8b55cf9610066d1bfb4a8df
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Tue Aug 7 16:45:27 2012 -0400

    Add Hebrew presentation forms shaping
    
    Lifted from https://bugzilla.mozilla.org/show_bug.cgi?id=728866

diff --git a/src/hb-unicode-private.hh b/src/hb-unicode-private.hh
index 5c5fff9..2a67f0a 100644
--- a/src/hb-unicode-private.hh
+++ b/src/hb-unicode-private.hh
@@ -80,6 +80,7 @@ HB_UNICODE_FUNCS_IMPLEMENT_CALLBACKS_SIMPLE
 			    hb_codepoint_t *ab)
   {
     *ab = 0;
+
     /* XXX, this belongs to indic normalizer. */
     if ((FLAG (general_category (a)) &
 	 (FLAG (HB_UNICODE_GENERAL_CATEGORY_SPACING_MARK) |
@@ -88,7 +89,125 @@ HB_UNICODE_FUNCS_IMPLEMENT_CALLBACKS_SIMPLE
       return false;
     /* XXX, add composition-exclusion exceptions to Indic shaper. */
     if (a == 0x09AF && b == 0x09BC) { *ab = 0x09DF; return true; }
-    return func.compose (this, a, b, ab, user_data.compose);
+
+    /* XXX, these belong to the hebew / default shaper. */
+    /* Hebrew presentation-form shaping.
+     * https://bugzilla.mozilla.org/show_bug.cgi?id=728866 */
+    // Hebrew presentation forms with dagesh, for characters 0x05D0..0x05EA;
+    // note that some letters do not have a dagesh presForm encoded
+    static const hb_codepoint_t sDageshForms[0x05EA - 0x05D0 + 1] = {
+      0xFB30, // ALEF
+      0xFB31, // BET
+      0xFB32, // GIMEL
+      0xFB33, // DALET
+      0xFB34, // HE
+      0xFB35, // VAV
+      0xFB36, // ZAYIN
+      0, // HET
+      0xFB38, // TET
+      0xFB39, // YOD
+      0xFB3A, // FINAL KAF
+      0xFB3B, // KAF
+      0xFB3C, // LAMED
+      0, // FINAL MEM
+      0xFB3E, // MEM
+      0, // FINAL NUN
+      0xFB40, // NUN
+      0xFB41, // SAMEKH
+      0, // AYIN
+      0xFB43, // FINAL PE
+      0xFB44, // PE
+      0, // FINAL TSADI
+      0xFB46, // TSADI
+      0xFB47, // QOF
+      0xFB48, // RESH
+      0xFB49, // SHIN
+      0xFB4A // TAV
+    };
+
+    hb_bool_t found = func.compose (this, a, b, ab, user_data.compose);
+
+    if (!found && (b & ~0x7F) == 0x0580) {
+	// special-case Hebrew presentation forms that are excluded from
+	// standard normalization, but wanted for old fonts
+	switch (b) {
+	case 0x05B4: // HIRIQ
+	    if (a == 0x05D9) { // YOD
+		*ab = 0xFB1D;
+		found = true;
+	    }
+	    break;
+	case 0x05B7: // patah
+	    if (a == 0x05F2) { // YIDDISH YOD YOD
+		*ab = 0xFB1F;
+		found = true;
+	    } else if (a == 0x05D0) { // ALEF
+		*ab = 0xFB2E;
+		found = true;
+	    }
+	    break;
+	case 0x05B8: // QAMATS
+	    if (a == 0x05D0) { // ALEF
+		*ab = 0xFB2F;
+		found = true;
+	    }
+	    break;
+	case 0x05B9: // HOLAM
+	    if (a == 0x05D5) { // VAV
+		*ab = 0xFB4B;
+		found = true;
+	    }
+	    break;
+	case 0x05BC: // DAGESH
+	    if (a >= 0x05D0 && a <= 0x05EA) {
+		*ab = sDageshForms[a - 0x05D0];
+		found = (*ab != 0);
+	    } else if (a == 0xFB2A) { // SHIN WITH SHIN DOT
+		*ab = 0xFB2C;
+		found = true;
+	    } else if (a == 0xFB2B) { // SHIN WITH SIN DOT
+		*ab = 0xFB2D;
+		found = true;
+	    }
+	    break;
+	case 0x05BF: // RAFE
+	    switch (a) {
+	    case 0x05D1: // BET
+		*ab = 0xFB4C;
+		found = true;
+		break;
+	    case 0x05DB: // KAF
+		*ab = 0xFB4D;
+		found = true;
+		break;
+	    case 0x05E4: // PE
+		*ab = 0xFB4E;
+		found = true;
+		break;
+	    }
+	    break;
+	case 0x05C1: // SHIN DOT
+	    if (a == 0x05E9) { // SHIN
+		*ab = 0xFB2A;
+		found = true;
+	    } else if (a == 0xFB49) { // SHIN WITH DAGESH
+		*ab = 0xFB2C;
+		found = true;
+	    }
+	    break;
+	case 0x05C2: // SIN DOT
+	    if (a == 0x05E9) { // SHIN
+		*ab = 0xFB2B;
+		found = true;
+	    } else if (a == 0xFB49) { // SHIN WITH DAGESH
+		*ab = 0xFB2D;
+		found = true;
+	    }
+	    break;
+	}
+    }
+
+    return found;
   }
 
   inline hb_bool_t decompose (hb_codepoint_t ab,
commit 32d71dc13317b322e2c5de00d767b2cb15fddd8b
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Tue Aug 7 14:11:16 2012 -0400

    [Graphite] Minor

diff --git a/src/hb-graphite2.cc b/src/hb-graphite2.cc
index 074ec36..70103d5 100644
--- a/src/hb-graphite2.cc
+++ b/src/hb-graphite2.cc
@@ -47,8 +47,8 @@ HB_SHAPER_DATA_ENSURE_DECLARE(graphite2, font)
  */
 
 typedef struct hb_graphite2_tablelist_t {
-  hb_blob_t *blob;
   struct hb_graphite2_tablelist_t *next;
+  hb_blob_t *blob;
   unsigned int tag;
 } hb_graphite2_tablelist_t;
 
@@ -58,40 +58,36 @@ struct hb_graphite2_shaper_face_data_t {
   hb_graphite2_tablelist_t *tlist;
 };
 
-static const void *hb_gr_get_table (const void *data, unsigned int tag, size_t *len)
+static const void *hb_graphite2_get_table (const void *data, unsigned int tag, size_t *len)
 {
-  hb_graphite2_tablelist_t *pl = NULL, *p;
-  hb_graphite2_shaper_face_data_t *face = (hb_graphite2_shaper_face_data_t *) data;
-  hb_graphite2_tablelist_t *tlist = face->tlist;
-
-  for (p = tlist; p; p = p->next)
-    if (p->tag == tag ) {
-      unsigned int tlen;
-      const char *d = hb_blob_get_data (p->blob, &tlen);
-      *len = tlen;
-      return d;
-    } else
-      pl = p;
-
-  hb_blob_t *blob = hb_face_reference_table (face->face, tag);
-
-  /* TODO Not thread-safe. */
-  if (!pl || pl->blob)
+  hb_graphite2_shaper_face_data_t *face_data = (hb_graphite2_shaper_face_data_t *) data;
+  hb_graphite2_tablelist_t *tlist = face_data->tlist;
+
+  hb_blob_t *blob = NULL;
+
+  for (hb_graphite2_tablelist_t *p = tlist; p; p = p->next)
+    if (p->tag == tag) {
+      blob = p->blob;
+      break;
+    }
+
+  if (unlikely (!blob))
   {
-    p = (hb_graphite2_tablelist_t *) calloc (1, sizeof (hb_graphite2_tablelist_t));
-    if (!p) {
+    blob = hb_face_reference_table (face_data->face, tag);
+
+    hb_graphite2_tablelist_t *p = (hb_graphite2_tablelist_t *) calloc (1, sizeof (hb_graphite2_tablelist_t));
+    if (unlikely (!p)) {
       hb_blob_destroy (blob);
       return NULL;
     }
-    p->next = NULL;
-    if (pl)
-      pl->next = p;
-    else
-      face->tlist = p;
-    pl = p;
+    p->blob = blob;
+    p->tag = tag;
+
+    /* TODO Not thread-safe, but fairly harmless.
+     * We can do the double-chcked pointer cmpexch thing here. */
+    p->next = face_data->tlist;
+    face_data->tlist = p;
   }
-  pl->blob = blob;
-  pl->tag = tag;
 
   unsigned int tlen;
   const char *d = hb_blob_get_data (blob, &tlen);
@@ -117,7 +113,7 @@ _hb_graphite2_shaper_face_data_create (hb_face_t *face)
     hb_blob_destroy (silf_blob);
 
   data->face = face;
-  data->grface = gr_make_face (data, &hb_gr_get_table, gr_face_default);
+  data->grface = gr_make_face (data, &hb_graphite2_get_table, gr_face_default);
 
   if (unlikely (!data->grface)) {
     free (data);
@@ -150,7 +146,7 @@ _hb_graphite2_shaper_face_data_destroy (hb_graphite2_shaper_face_data_t *data)
  * shaper font data
  */
 
-static float hb_gr_get_advance (const void *hb_font, unsigned short gid)
+static float hb_graphite2_get_advance (const void *hb_font, unsigned short gid)
 {
   return ((hb_font_t *) hb_font)->get_glyph_h_advance (gid);
 }
@@ -163,7 +159,7 @@ _hb_graphite2_shaper_font_data_create (hb_font_t *font)
   hb_face_t *face = font->face;
   hb_graphite2_shaper_face_data_t *face_data = HB_SHAPER_DATA_GET (face);
 
-  return gr_make_font_with_advance_fn (font->x_scale, font, &hb_gr_get_advance, face_data->grface);
+  return gr_make_font_with_advance_fn (font->x_scale, font, &hb_graphite2_get_advance, face_data->grface);
 }
 
 void



More information about the HarfBuzz mailing list