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

Behdad Esfahbod behdad at kemper.freedesktop.org
Tue Oct 12 09:35:17 PDT 2010


 TODO                            |    2 
 autogen.sh                      |    3 
 configure.ac                    |    1 
 src/Makefile.am                 |   12 
 src/check-c-linkage-decls.sh    |    2 
 src/check-header-guards.sh      |    2 
 src/gen-arabic-joining-table.py |   39 ++
 src/hb-buffer-private.hh        |   14 
 src/hb-buffer.cc                |    1 
 src/hb-font.cc                  |   76 ++++
 src/hb-font.h                   |   60 +++
 src/hb-open-type-private.hh     |   13 
 src/hb-ot-layout-private.hh     |   12 
 src/hb-ot-map-private.hh        |  156 ++++++++
 src/hb-ot-map.cc                |  165 +++++++++
 src/hb-ot-shape-arabic.cc       |  706 ++++++++++++++++++++++++++++++++++++++++
 src/hb-ot-shape-private.hh      |   60 +++
 src/hb-ot-shape.cc              |  532 ++++++------------------------
 src/hb-ot-tag.c                 |   17 
 src/hb-private.h                |    4 
 src/hb-unicode.c                |   38 ++
 src/hb-unicode.h                |   33 +
 22 files changed, 1502 insertions(+), 446 deletions(-)

New commits:
commit f4792d99eee0e8cd72b7cb01c96a09f16e2a72ce
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Tue Oct 12 12:32:18 2010 -0400

    Fix infinite loop!
    
    Untested code is indeed buggy code.
    
    Mozilla bug #603352.

diff --git a/src/hb-ot-tag.c b/src/hb-ot-tag.c
index 32d455d..c325802 100644
--- a/src/hb-ot-tag.c
+++ b/src/hb-ot-tag.c
@@ -630,13 +630,11 @@ hb_ot_tag_from_language (hb_language_t language)
     char tag[4];
     int i;
     lang_str += 6;
-    i = 0;
 #define IS_LETTER(c) (((c) >= 'a' && (c) <= 'z') || ((c) >= 'A' && (c) <= 'Z'))
 #define TO_UPPER(c) (((c) >= 'a' && (c) <= 'z') ? (c) + 'A' - 'a' : (c))
-    while (i < 4 && IS_LETTER (lang_str[i])) {
+    for (i = 0; i < 4 && IS_LETTER (lang_str[i]); i++)
       tag[i] = TO_UPPER (lang_str[i]);
-    }
-    while (i < 4)
+    for (; i < 4; i++)
       tag[i] = ' ';
     return HB_TAG_STR (tag);
   }
commit 993d1e786a32612b796dae8309ce402a4121bec7
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Tue Oct 12 11:17:30 2010 -0400

    Fix missing negation in unreachable code!
    
    Mozilla bug #603346

diff --git a/src/hb-open-type-private.hh b/src/hb-open-type-private.hh
index ea1d371..38ba3a3 100644
--- a/src/hb-open-type-private.hh
+++ b/src/hb-open-type-private.hh
@@ -587,7 +587,7 @@ struct GenericArrayOf
      * other structs. */
     unsigned int count = len;
     for (unsigned int i = 0; i < count; i++)
-      if (array[i].sanitize (c))
+      if (unlikely (!array[i].sanitize (c)))
         return false;
     return true;
   }
@@ -697,9 +697,8 @@ struct HeadlessArrayOf
      * to do have a simple sanitize(), ie. they do not reference
      * other structs. */
     unsigned int count = len ? len - 1 : 0;
-    Type *a = array;
     for (unsigned int i = 0; i < count; i++)
-      if (unlikely (!a[i].sanitize (c)))
+      if (unlikely (!array[i].sanitize (c)))
         return false;
     return true;
   }
commit 52601275d5e4000dada4f925fb78723eeeee7bd4
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Fri Oct 8 20:38:46 2010 -0400

    More separation

diff --git a/src/hb-ot-shape.cc b/src/hb-ot-shape.cc
index e11f1c6..8fc9c4f 100644
--- a/src/hb-ot-shape.cc
+++ b/src/hb-ot-shape.cc
@@ -79,15 +79,8 @@ hb_ot_shape_collect_features (hb_ot_shape_context_t *c)
 
 
 static void
-hb_ot_shape_setup_lookups (hb_ot_shape_context_t *c)
+hb_ot_shape_setup_masks (hb_ot_shape_context_t *c)
 {
-  hb_ot_shape_collect_features (c);
-
-  /* Compile features */
-  c->map->compile (c);
-
-  /* Set masks in buffer */
-
   hb_mask_t global_mask = c->map->get_global_mask ();
   if (global_mask)
     c->buffer->set_masks (global_mask, global_mask, 0, (unsigned int) -1);
@@ -278,7 +271,7 @@ hb_position_complex_fallback_visual (hb_ot_shape_context_t *c)
 static void
 hb_ot_shape_internal (hb_ot_shape_context_t *c)
 {
-  hb_ot_shape_setup_lookups (c);
+  hb_ot_shape_setup_masks (c);
 
   hb_form_clusters (c);
 
@@ -335,6 +328,9 @@ hb_ot_shape (hb_font_t    *font,
   c.original_direction = buffer->props.direction;
   c.map = ↦
 
+  hb_ot_shape_collect_features (&c);
+  c.map->compile (&c);
+
   hb_ot_shape_internal (&c);
 }
 
commit 6b7e6758626268ba1c7c266128e618ec73ae2c0f
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Fri Oct 8 20:30:04 2010 -0400

    Minor

diff --git a/src/hb-ot-shape.cc b/src/hb-ot-shape.cc
index 103a497..e11f1c6 100644
--- a/src/hb-ot-shape.cc
+++ b/src/hb-ot-shape.cc
@@ -147,25 +147,25 @@ is_variation_selector (hb_codepoint_t unicode)
 }
 
 static void
-hb_form_clusters (hb_buffer_t *buffer)
+hb_form_clusters (hb_ot_shape_context_t *c)
 {
-  unsigned int count = buffer->len;
+  unsigned int count = c->buffer->len;
   for (unsigned int i = 1; i < count; i++)
-    if (buffer->unicode->v.get_general_category (buffer->info[i].codepoint) == HB_CATEGORY_NON_SPACING_MARK)
-      buffer->info[i].cluster = buffer->info[i - 1].cluster;
+    if (c->buffer->unicode->v.get_general_category (c->buffer->info[i].codepoint) == HB_CATEGORY_NON_SPACING_MARK)
+      c->buffer->info[i].cluster = c->buffer->info[i - 1].cluster;
 }
 
 static void
-hb_ensure_native_direction (hb_buffer_t *buffer)
+hb_ensure_native_direction (hb_ot_shape_context_t *c)
 {
-  hb_direction_t direction = buffer->props.direction;
+  hb_direction_t direction = c->buffer->props.direction;
 
   /* TODO vertical */
   if (HB_DIRECTION_IS_HORIZONTAL (direction) &&
-      direction != _hb_script_get_horizontal_direction (buffer->props.script))
+      direction != _hb_script_get_horizontal_direction (c->buffer->props.script))
   {
-    hb_buffer_reverse_clusters (buffer);
-    buffer->props.direction = HB_DIRECTION_REVERSE (buffer->props.direction);
+    hb_buffer_reverse_clusters (c->buffer);
+    c->buffer->props.direction = HB_DIRECTION_REVERSE (c->buffer->props.direction);
   }
 }
 
@@ -280,7 +280,7 @@ hb_ot_shape_internal (hb_ot_shape_context_t *c)
 {
   hb_ot_shape_setup_lookups (c);
 
-  hb_form_clusters (c->buffer);
+  hb_form_clusters (c);
 
   /* SUBSTITUTE */
   {
@@ -289,7 +289,7 @@ hb_ot_shape_internal (hb_ot_shape_context_t *c)
     /* Mirroring needs to see the original direction */
     hb_mirror_chars (c);
 
-    hb_ensure_native_direction (c->buffer);
+    hb_ensure_native_direction (c);
 
     hb_substitute_default (c);
 
commit aa9c450bb2d7c3c9e36ea32e3558250391a0582d
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Fri Oct 8 20:27:38 2010 -0400

    Enable 'rtlm' mirroring

diff --git a/src/hb-ot-map-private.hh b/src/hb-ot-map-private.hh
index 29b9c32..816ce4f 100644
--- a/src/hb-ot-map-private.hh
+++ b/src/hb-ot-map-private.hh
@@ -105,9 +105,7 @@ struct hb_ot_map_t {
 
   hb_ot_map_t (void) : feature_count (0) {}
 
-  void add_feature (hb_tag_t tag,
-		    unsigned int value,
-		    bool global)
+  void add_feature (hb_tag_t tag, unsigned int value, bool global)
   {
     feature_info_t *info = &feature_infos[feature_count++];
     info->tag = tag;
@@ -116,11 +114,14 @@ struct hb_ot_map_t {
     info->global = global;
   }
 
+  inline void add_bool_feature (hb_tag_t tag, bool global = true)
+  { add_feature (tag, 1, global); }
+
   HB_INTERNAL void compile (hb_ot_shape_context_t *c);
 
   hb_mask_t get_global_mask (void) const { return global_mask; }
 
-  hb_mask_t get_mask (hb_tag_t tag, unsigned int *shift) const {
+  hb_mask_t get_mask (hb_tag_t tag, unsigned int *shift = NULL) const {
     const feature_map_t *map = (const feature_map_t *) bsearch (&tag, feature_maps, feature_count, sizeof (feature_maps[0]), (hb_compare_func_t) feature_map_t::cmp);
     if (shift) *shift = map ? map->shift : 0;
     return map ? map->mask : 0;
diff --git a/src/hb-ot-shape.cc b/src/hb-ot-shape.cc
index b43f697..103a497 100644
--- a/src/hb-ot-shape.cc
+++ b/src/hb-ot-shape.cc
@@ -53,12 +53,12 @@ hb_ot_shape_collect_features (hb_ot_shape_context_t *c)
 {
   switch (c->original_direction) {
     case HB_DIRECTION_LTR:
-      c->map->add_feature (HB_TAG ('l','t','r','a'), 1, true);
-      c->map->add_feature (HB_TAG ('l','t','r','m'), 1, true);
+      c->map->add_bool_feature (HB_TAG ('l','t','r','a'));
+      c->map->add_bool_feature (HB_TAG ('l','t','r','m'));
       break;
     case HB_DIRECTION_RTL:
-      c->map->add_feature (HB_TAG ('r','t','l','a'), 1, true);
-      c->map->add_feature (HB_TAG ('r','t','l','m'), 1, false);
+      c->map->add_bool_feature (HB_TAG ('r','t','l','a'));
+      c->map->add_bool_feature (HB_TAG ('r','t','l','m'), false);
       break;
     case HB_DIRECTION_TTB:
     case HB_DIRECTION_BTT:
@@ -67,7 +67,7 @@ hb_ot_shape_collect_features (hb_ot_shape_context_t *c)
   }
 
   for (unsigned int i = 0; i < ARRAY_LENGTH (default_features); i++)
-    c->map->add_feature (default_features[i], 1, true);
+    c->map->add_bool_feature (default_features[i]);
 
   /* complex */
 
@@ -173,22 +173,22 @@ hb_ensure_native_direction (hb_buffer_t *buffer)
 /* Substitute */
 
 static void
-hb_mirror_chars (hb_buffer_t *buffer)
+hb_mirror_chars (hb_ot_shape_context_t *c)
 {
-  hb_unicode_get_mirroring_func_t get_mirroring = buffer->unicode->v.get_mirroring;
+  hb_unicode_get_mirroring_func_t get_mirroring = c->buffer->unicode->v.get_mirroring;
 
-  if (HB_DIRECTION_IS_FORWARD (buffer->props.direction))
+  if (HB_DIRECTION_IS_FORWARD (c->buffer->props.direction))
     return;
 
-//  map = c->map.find_feature (HB_TAG ('r','t','l','m'));
+  hb_mask_t rtlm_mask = c->map->get_mask (HB_TAG ('r','t','l','m'));
 
-  unsigned int count = buffer->len;
+  unsigned int count = c->buffer->len;
   for (unsigned int i = 0; i < count; i++) {
-    hb_codepoint_t codepoint = get_mirroring (buffer->info[i].codepoint);
-    if (likely (codepoint == buffer->info[i].codepoint))
-;//      buffer->info[i].mask |= map->mask;
+    hb_codepoint_t codepoint = get_mirroring (c->buffer->info[i].codepoint);
+    if (likely (codepoint == c->buffer->info[i].codepoint))
+      c->buffer->info[i].mask |= rtlm_mask;
     else
-      buffer->info[i].codepoint = codepoint;
+      c->buffer->info[i].codepoint = codepoint;
   }
 }
 
@@ -287,7 +287,7 @@ hb_ot_shape_internal (hb_ot_shape_context_t *c)
     c->buffer->clear_masks ();
 
     /* Mirroring needs to see the original direction */
-    hb_mirror_chars (c->buffer);
+    hb_mirror_chars (c);
 
     hb_ensure_native_direction (c->buffer);
 
commit 36925f695d349a53d52ecc3a58f18240a6977463
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Fri Oct 8 20:20:32 2010 -0400

    Rename hb_mask_allocator_t to hb_ot_map_t
    
    hb_ot_plan_t may be a better name, donno.

diff --git a/src/hb-ot-map-private.hh b/src/hb-ot-map-private.hh
index 9bd4bf1..29b9c32 100644
--- a/src/hb-ot-map-private.hh
+++ b/src/hb-ot-map-private.hh
@@ -41,7 +41,7 @@ HB_BEGIN_DECLS
 
 static const hb_tag_t table_tags[2] = {HB_OT_TAG_GSUB, HB_OT_TAG_GPOS};
 
-struct hb_mask_allocator_t {
+struct hb_ot_map_t {
 
   private:
 
@@ -103,7 +103,7 @@ struct hb_mask_allocator_t {
 
   public:
 
-  hb_mask_allocator_t (void) : feature_count (0) {}
+  hb_ot_map_t (void) : feature_count (0) {}
 
   void add_feature (hb_tag_t tag,
 		    unsigned int value,
diff --git a/src/hb-ot-map.cc b/src/hb-ot-map.cc
index 2092a77..8cf21b4 100644
--- a/src/hb-ot-map.cc
+++ b/src/hb-ot-map.cc
@@ -34,7 +34,7 @@ HB_BEGIN_DECLS
 
 
 void
-hb_mask_allocator_t::compile (hb_ot_shape_context_t *c)
+hb_ot_map_t::compile (hb_ot_shape_context_t *c)
 {
  global_mask = 0;
  lookup_count[0] = lookup_count[1] = 0;
diff --git a/src/hb-ot-shape-private.hh b/src/hb-ot-shape-private.hh
index c8a0295..b00be33 100644
--- a/src/hb-ot-shape-private.hh
+++ b/src/hb-ot-shape-private.hh
@@ -35,7 +35,7 @@
 
 HB_BEGIN_DECLS
 
-struct hb_mask_allocator_t;
+struct hb_ot_map_t;
 
 struct hb_ot_shape_context_t
 {
@@ -51,7 +51,7 @@ struct hb_ot_shape_context_t
   hb_bool_t applied_substitute_complex;
   hb_bool_t applied_position_complex;
 
-  struct hb_mask_allocator_t *allocator;
+  struct hb_ot_map_t *map;
 };
 
 
diff --git a/src/hb-ot-shape.cc b/src/hb-ot-shape.cc
index aff1476..b43f697 100644
--- a/src/hb-ot-shape.cc
+++ b/src/hb-ot-shape.cc
@@ -49,17 +49,16 @@ hb_tag_t default_features[] = {
 };
 
 static void
-hb_ot_shape_collect_features (hb_ot_shape_context_t *c,
-			      hb_mask_allocator_t   *allocator)
+hb_ot_shape_collect_features (hb_ot_shape_context_t *c)
 {
   switch (c->original_direction) {
     case HB_DIRECTION_LTR:
-      allocator->add_feature (HB_TAG ('l','t','r','a'), 1, true);
-      allocator->add_feature (HB_TAG ('l','t','r','m'), 1, true);
+      c->map->add_feature (HB_TAG ('l','t','r','a'), 1, true);
+      c->map->add_feature (HB_TAG ('l','t','r','m'), 1, true);
       break;
     case HB_DIRECTION_RTL:
-      allocator->add_feature (HB_TAG ('r','t','l','a'), 1, true);
-      allocator->add_feature (HB_TAG ('r','t','l','m'), 1, false);
+      c->map->add_feature (HB_TAG ('r','t','l','a'), 1, true);
+      c->map->add_feature (HB_TAG ('r','t','l','m'), 1, false);
       break;
     case HB_DIRECTION_TTB:
     case HB_DIRECTION_BTT:
@@ -68,29 +67,28 @@ hb_ot_shape_collect_features (hb_ot_shape_context_t *c,
   }
 
   for (unsigned int i = 0; i < ARRAY_LENGTH (default_features); i++)
-    allocator->add_feature (default_features[i], 1, true);
+    c->map->add_feature (default_features[i], 1, true);
 
   /* complex */
 
   for (unsigned int i = 0; i < c->num_features; i++) {
     const hb_feature_t *feature = &c->features[i];
-    allocator->add_feature (feature->tag, feature->value, (feature->start == 0 && feature->end == (unsigned int) -1));
+    c->map->add_feature (feature->tag, feature->value, (feature->start == 0 && feature->end == (unsigned int) -1));
   }
 }
 
 
 static void
-hb_ot_shape_setup_lookups (hb_ot_shape_context_t *c,
-			   hb_mask_allocator_t   *allocator)
+hb_ot_shape_setup_lookups (hb_ot_shape_context_t *c)
 {
-  hb_ot_shape_collect_features (c, allocator);
+  hb_ot_shape_collect_features (c);
 
   /* Compile features */
-  allocator->compile (c);
+  c->map->compile (c);
 
   /* Set masks in buffer */
 
-  hb_mask_t global_mask = allocator->get_global_mask ();
+  hb_mask_t global_mask = c->map->get_global_mask ();
   if (global_mask)
     c->buffer->set_masks (global_mask, global_mask, 0, (unsigned int) -1);
 
@@ -99,7 +97,7 @@ hb_ot_shape_setup_lookups (hb_ot_shape_context_t *c,
     hb_feature_t *feature = &c->features[i];
     if (!(feature->start == 0 && feature->end == (unsigned int)-1)) {
       unsigned int shift;
-      hb_mask_t mask = allocator->get_mask (feature->tag, &shift);
+      hb_mask_t mask = c->map->get_mask (feature->tag, &shift);
       c->buffer->set_masks (feature->value << shift, mask, feature->start, feature->end);
     }
   }
@@ -109,27 +107,25 @@ hb_ot_shape_setup_lookups (hb_ot_shape_context_t *c,
 
 
 static void
-hb_ot_substitute_complex (hb_ot_shape_context_t *c,
-			  const hb_mask_allocator_t *allocator)
+hb_ot_substitute_complex (hb_ot_shape_context_t *c)
 {
   if (!hb_ot_layout_has_substitution (c->face))
     return;
 
-  allocator->substitute (c);
+  c->map->substitute (c);
 
   c->applied_substitute_complex = TRUE;
   return;
 }
 
 static void
-hb_ot_position_complex (hb_ot_shape_context_t *c,
-			const hb_mask_allocator_t *allocator)
+hb_ot_position_complex (hb_ot_shape_context_t *c)
 {
 
   if (!hb_ot_layout_has_positioning (c->face))
     return;
 
-  allocator->position (c);
+  c->map->position (c);
 
   hb_ot_layout_position_finish (c->font, c->face, c->buffer);
 
@@ -184,7 +180,7 @@ hb_mirror_chars (hb_buffer_t *buffer)
   if (HB_DIRECTION_IS_FORWARD (buffer->props.direction))
     return;
 
-//  map = allocator.find_feature (HB_TAG ('r','t','l','m'));
+//  map = c->map.find_feature (HB_TAG ('r','t','l','m'));
 
   unsigned int count = buffer->len;
   for (unsigned int i = 0; i < count; i++) {
@@ -282,10 +278,7 @@ hb_position_complex_fallback_visual (hb_ot_shape_context_t *c)
 static void
 hb_ot_shape_internal (hb_ot_shape_context_t *c)
 {
-  hb_mask_allocator_t allocator;
-
-  hb_ot_shape_setup_lookups (c, &allocator);
-
+  hb_ot_shape_setup_lookups (c);
 
   hb_form_clusters (c->buffer);
 
@@ -300,7 +293,7 @@ hb_ot_shape_internal (hb_ot_shape_context_t *c)
 
     hb_substitute_default (c);
 
-    hb_ot_substitute_complex (c, &allocator);
+    hb_ot_substitute_complex (c);
 
     if (!c->applied_substitute_complex)
       hb_substitute_complex_fallback (c);
@@ -312,7 +305,7 @@ hb_ot_shape_internal (hb_ot_shape_context_t *c)
 
     hb_position_default (c);
 
-    hb_ot_position_complex (c, &allocator);
+    hb_ot_position_complex (c);
 
     hb_bool_t position_fallback = !c->applied_position_complex;
     if (position_fallback)
@@ -336,9 +329,11 @@ hb_ot_shape (hb_font_t    *font,
 	     unsigned int  num_features)
 {
   hb_ot_shape_context_t c = {font, face, buffer, features, num_features};
+  hb_ot_map_t map;
 
   /* Setup transient context members */
   c.original_direction = buffer->props.direction;
+  c.map = ↦
 
   hb_ot_shape_internal (&c);
 }
commit f5dd3be46b5c77a2c5b97b82a0b67ac9e851b898
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Fri Oct 8 20:16:23 2010 -0400

    Improve checks

diff --git a/src/check-c-linkage-decls.sh b/src/check-c-linkage-decls.sh
index c262516..711d089 100755
--- a/src/check-c-linkage-decls.sh
+++ b/src/check-c-linkage-decls.sh
@@ -8,7 +8,7 @@ stat=0
 
 cd "$srcdir"
 
-for x in *.c *.cc *.h *.hh ; do
+for x in hb-*.c hb-*.cc hb-*.h hb-*.hh ; do
 	if ! grep -q HB_BEGIN_DECLS "$x" || ! grep -q HB_END_DECLS "$x"; then
 		echo "Ouch, file $x does not HB_BEGIN_DECLS / HB_END_DECLS"
 		stat=1
diff --git a/src/check-header-guards.sh b/src/check-header-guards.sh
index 59c399a..c966cd2 100755
--- a/src/check-header-guards.sh
+++ b/src/check-header-guards.sh
@@ -8,7 +8,7 @@ stat=0
 
 cd "$srcdir"
 
-for x in *.h *.hh ; do
+for x in hb-*.h hb-*.hh ; do
 	tag=`echo "$x" | tr 'a-z.-' 'A-Z_'`
 	lines=`grep "\<$tag\>" "$x" | wc -l`
 	if test "x$lines" != x3; then
commit 5a2b0b3878cd9c62121bb4fd6344e102a9ee1825
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Fri Oct 8 20:14:57 2010 -0400

    Add hb-ot-map.cc

diff --git a/src/Makefile.am b/src/Makefile.am
index c47a1a5..4908876 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -47,6 +47,7 @@ HBSOURCES += \
 	hb-ot-layout-gsubgpos-private.hh \
 	hb-ot-layout-gsub-private.hh \
 	hb-ot-layout-private.hh \
+	hb-ot-map.cc \
 	hb-ot-map-private.hh \
 	hb-ot-shape.cc \
 	hb-ot-shape-private.hh \
diff --git a/src/hb-ot-map-private.hh b/src/hb-ot-map-private.hh
index 4d1deef..9bd4bf1 100644
--- a/src/hb-ot-map-private.hh
+++ b/src/hb-ot-map-private.hh
@@ -116,134 +116,9 @@ struct hb_mask_allocator_t {
     info->global = global;
   }
 
-  void compile (hb_ot_shape_context_t *c)
-  {
-   global_mask = 0;
-   lookup_count[0] = lookup_count[1] = 0;
-
-    if (!feature_count)
-      return;
-
-
-    /* Fetch script/language indices for GSUB/GPOS.  We need these later to skip
-     * features not available in either table and not waste precious bits for them. */
-
-    const hb_tag_t *script_tags;
-    hb_tag_t language_tag;
-
-    script_tags = hb_ot_tags_from_script (c->buffer->props.script);
-    language_tag = hb_ot_tag_from_language (c->buffer->props.language);
-
-    unsigned int script_index[2], language_index[2];
-    for (unsigned int table_index = 0; table_index < 2; table_index++) {
-      hb_tag_t table_tag = table_tags[table_index];
-      hb_ot_layout_table_choose_script (c->face, table_tag, script_tags, &script_index[table_index]);
-      hb_ot_layout_script_find_language (c->face, table_tag, script_index[table_index], language_tag, &language_index[table_index]);
-    }
-
-
-    /* Sort features and merge duplicates */
-    qsort (feature_infos, feature_count, sizeof (feature_infos[0]), (hb_compare_func_t) feature_info_t::cmp);
-    unsigned int j = 0;
-    for (unsigned int i = 1; i < feature_count; i++)
-      if (feature_infos[i].tag != feature_infos[j].tag)
-	feature_infos[++j] = feature_infos[i];
-      else {
-	if (feature_infos[i].global)
-	  feature_infos[j] = feature_infos[i];
-	else {
-	  feature_infos[j].global = false;
-	  feature_infos[j].value = MAX (feature_infos[j].value, feature_infos[i].value);
-	}
-      }
-    feature_count = j + 1;
-
-
-    /* Allocate bits now */
-    unsigned int next_bit = 1;
-    j = 0;
-    for (unsigned int i = 0; i < feature_count; i++) {
-      const feature_info_t *info = &feature_infos[i];
-
-      unsigned int bits_needed;
-
-      if (info->global && info->value == 1)
-        /* Uses the global bit */
-        bits_needed = 0;
-      else
-        bits_needed = _hb_bit_storage (info->value);
-
-      if (!info->value || next_bit + bits_needed > 8 * sizeof (hb_mask_t))
-        continue; /* Feature disabled, or not enough bits. */
-
-
-      bool found = false;
-      unsigned int feature_index[2];
-      for (unsigned int table_index = 0; table_index < 2; table_index++)
-        found |= hb_ot_layout_language_find_feature (c->face,
-						     table_tags[table_index],
-						     script_index[table_index],
-						     language_index[table_index],
-						     info->tag,
-						     &feature_index[table_index]);
-      if (!found)
-        continue;
-
-
-      feature_map_t *map = &feature_maps[j++];
-
-      map->tag = info->tag;
-      map->index[0] = feature_index[0];
-      map->index[1] = feature_index[1];
-      if (info->global && info->value == 1) {
-        /* Uses the global bit */
-        map->shift = 0;
-	map->mask = 1;
-      } else {
-	map->shift = next_bit;
-	map->mask = (1 << (next_bit + bits_needed)) - (1 << next_bit);
-	next_bit += bits_needed;
-	if (info->global)
-	  global_mask |= map->mask;
-      }
-
-    }
-    feature_count = j;
-
-
-    for (unsigned int table_index = 0; table_index < 2; table_index++) {
-      hb_tag_t table_tag = table_tags[table_index];
-
-      /* Collect lookup indices for features */
-
-      unsigned int required_feature_index;
-      if (hb_ot_layout_language_get_required_feature_index (c->face,
-							    table_tag,
-							    script_index[table_index],
-							    language_index[table_index],
-							    &required_feature_index))
-	add_lookups (c, table_index, required_feature_index, 1);
-
-      for (unsigned i = 0; i < feature_count; i++)
-	add_lookups (c, table_index, feature_maps[i].index[table_index], feature_maps[i].mask);
-
-      /* Sort lookups and merge duplicates */
-      qsort (lookup_maps[table_index], lookup_count[table_index], sizeof (lookup_maps[table_index][0]), (hb_compare_func_t) lookup_map_t::cmp);
-      if (lookup_count[table_index])
-      {
-	unsigned int j = 0;
-	for (unsigned int i = 1; i < lookup_count[table_index]; i++)
-	  if (lookup_maps[table_index][i].index != lookup_maps[table_index][j].index)
-	    lookup_maps[table_index][++j] = lookup_maps[table_index][i];
-	  else
-	    lookup_maps[table_index][j].mask |= lookup_maps[table_index][i].mask;
-	j++;
-	lookup_count[table_index] = j;
-      }
-    }
-  }
+  HB_INTERNAL void compile (hb_ot_shape_context_t *c);
 
-  hb_mask_t get_global_mask (void) { return global_mask; }
+  hb_mask_t get_global_mask (void) const { return global_mask; }
 
   hb_mask_t get_mask (hb_tag_t tag, unsigned int *shift) const {
     const feature_map_t *map = (const feature_map_t *) bsearch (&tag, feature_maps, feature_count, sizeof (feature_maps[0]), (hb_compare_func_t) feature_map_t::cmp);
diff --git a/src/hb-ot-map.cc b/src/hb-ot-map.cc
new file mode 100644
index 0000000..2092a77
--- /dev/null
+++ b/src/hb-ot-map.cc
@@ -0,0 +1,165 @@
+/*
+ * Copyright (C) 2009,2010  Red Hat, Inc.
+ * Copyright (C) 2010  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.
+ *
+ * Red Hat Author(s): Behdad Esfahbod
+ * Google Author(s): Behdad Esfahbod
+ */
+
+#include "hb-ot-map-private.hh"
+
+#include "hb-ot-shape-private.hh"
+
+HB_BEGIN_DECLS
+
+
+void
+hb_mask_allocator_t::compile (hb_ot_shape_context_t *c)
+{
+ global_mask = 0;
+ lookup_count[0] = lookup_count[1] = 0;
+
+  if (!feature_count)
+    return;
+
+
+  /* Fetch script/language indices for GSUB/GPOS.  We need these later to skip
+   * features not available in either table and not waste precious bits for them. */
+
+  const hb_tag_t *script_tags;
+  hb_tag_t language_tag;
+
+  script_tags = hb_ot_tags_from_script (c->buffer->props.script);
+  language_tag = hb_ot_tag_from_language (c->buffer->props.language);
+
+  unsigned int script_index[2], language_index[2];
+  for (unsigned int table_index = 0; table_index < 2; table_index++) {
+    hb_tag_t table_tag = table_tags[table_index];
+    hb_ot_layout_table_choose_script (c->face, table_tag, script_tags, &script_index[table_index]);
+    hb_ot_layout_script_find_language (c->face, table_tag, script_index[table_index], language_tag, &language_index[table_index]);
+  }
+
+
+  /* Sort features and merge duplicates */
+  qsort (feature_infos, feature_count, sizeof (feature_infos[0]), (hb_compare_func_t) feature_info_t::cmp);
+  unsigned int j = 0;
+  for (unsigned int i = 1; i < feature_count; i++)
+    if (feature_infos[i].tag != feature_infos[j].tag)
+      feature_infos[++j] = feature_infos[i];
+    else {
+      if (feature_infos[i].global)
+	feature_infos[j] = feature_infos[i];
+      else {
+	feature_infos[j].global = false;
+	feature_infos[j].value = MAX (feature_infos[j].value, feature_infos[i].value);
+      }
+    }
+  feature_count = j + 1;
+
+
+  /* Allocate bits now */
+  unsigned int next_bit = 1;
+  j = 0;
+  for (unsigned int i = 0; i < feature_count; i++) {
+    const feature_info_t *info = &feature_infos[i];
+
+    unsigned int bits_needed;
+
+    if (info->global && info->value == 1)
+      /* Uses the global bit */
+      bits_needed = 0;
+    else
+      bits_needed = _hb_bit_storage (info->value);
+
+    if (!info->value || next_bit + bits_needed > 8 * sizeof (hb_mask_t))
+      continue; /* Feature disabled, or not enough bits. */
+
+
+    bool found = false;
+    unsigned int feature_index[2];
+    for (unsigned int table_index = 0; table_index < 2; table_index++)
+      found |= hb_ot_layout_language_find_feature (c->face,
+						   table_tags[table_index],
+						   script_index[table_index],
+						   language_index[table_index],
+						   info->tag,
+						   &feature_index[table_index]);
+    if (!found)
+      continue;
+
+
+    feature_map_t *map = &feature_maps[j++];
+
+    map->tag = info->tag;
+    map->index[0] = feature_index[0];
+    map->index[1] = feature_index[1];
+    if (info->global && info->value == 1) {
+      /* Uses the global bit */
+      map->shift = 0;
+      map->mask = 1;
+    } else {
+      map->shift = next_bit;
+      map->mask = (1 << (next_bit + bits_needed)) - (1 << next_bit);
+      next_bit += bits_needed;
+      if (info->global)
+	global_mask |= map->mask;
+    }
+
+  }
+  feature_count = j;
+
+
+  for (unsigned int table_index = 0; table_index < 2; table_index++) {
+    hb_tag_t table_tag = table_tags[table_index];
+
+    /* Collect lookup indices for features */
+
+    unsigned int required_feature_index;
+    if (hb_ot_layout_language_get_required_feature_index (c->face,
+							  table_tag,
+							  script_index[table_index],
+							  language_index[table_index],
+							  &required_feature_index))
+      add_lookups (c, table_index, required_feature_index, 1);
+
+    for (unsigned i = 0; i < feature_count; i++)
+      add_lookups (c, table_index, feature_maps[i].index[table_index], feature_maps[i].mask);
+
+    /* Sort lookups and merge duplicates */
+    qsort (lookup_maps[table_index], lookup_count[table_index], sizeof (lookup_maps[table_index][0]), (hb_compare_func_t) lookup_map_t::cmp);
+    if (lookup_count[table_index])
+    {
+      unsigned int j = 0;
+      for (unsigned int i = 1; i < lookup_count[table_index]; i++)
+	if (lookup_maps[table_index][i].index != lookup_maps[table_index][j].index)
+	  lookup_maps[table_index][++j] = lookup_maps[table_index][i];
+	else
+	  lookup_maps[table_index][j].mask |= lookup_maps[table_index][i].mask;
+      j++;
+      lookup_count[table_index] = j;
+    }
+  }
+}
+
+
+HB_END_DECLS
commit ecc4550ed7bc900a61081edfbcd0ad09cbf29b36
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Fri Oct 8 20:05:29 2010 -0400

    Fix feature overriding

diff --git a/src/hb-ot-map-private.hh b/src/hb-ot-map-private.hh
index 2d790b0..4d1deef 100644
--- a/src/hb-ot-map-private.hh
+++ b/src/hb-ot-map-private.hh
@@ -152,7 +152,7 @@ struct hb_mask_allocator_t {
 	if (feature_infos[i].global)
 	  feature_infos[j] = feature_infos[i];
 	else {
-	  feature_infos[j].global = feature_infos[j].global && (feature_infos[j].value == feature_infos[i].value);
+	  feature_infos[j].global = false;
 	  feature_infos[j].value = MAX (feature_infos[j].value, feature_infos[i].value);
 	}
       }
commit 8f08c3275040870a645ef034a38d30c05c619f21
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Fri Oct 8 19:43:48 2010 -0400

    Minor cleanup

diff --git a/src/hb-open-type-private.hh b/src/hb-open-type-private.hh
index b83a2e3..ea1d371 100644
--- a/src/hb-open-type-private.hh
+++ b/src/hb-open-type-private.hh
@@ -718,13 +718,9 @@ struct SortedArrayOf : ArrayOf<Type> {
   template <typename SearchType>
   inline int search (const SearchType &x) const {
     class Cmp {
-      public: static int cmp (const void *p1, const void *p2) {
-	const SearchType *a = reinterpret_cast<const SearchType *>(p1);
-	const Type *b = reinterpret_cast<const Type *>(p2);
-	return b->cmp (*a);
-      }
+      public: static int cmp (const SearchType *a, const Type *b) { return b->cmp (*a); }
     };
-    const Type *p = (const Type *) bsearch (&x, this->array, this->len, sizeof (this->array[0]), Cmp::cmp);
+    const Type *p = (const Type *) bsearch (&x, this->array, this->len, sizeof (this->array[0]), (hb_compare_func_t) Cmp::cmp);
     return p ? p - this->array : -1;
   }
 };
diff --git a/src/hb-ot-map-private.hh b/src/hb-ot-map-private.hh
index 786a0d0..2d790b0 100644
--- a/src/hb-ot-map-private.hh
+++ b/src/hb-ot-map-private.hh
@@ -43,23 +43,16 @@ static const hb_tag_t table_tags[2] = {HB_OT_TAG_GSUB, HB_OT_TAG_GPOS};
 
 struct hb_mask_allocator_t {
 
+  private:
+
   struct feature_info_t {
     hb_tag_t tag;
     unsigned int value;
     unsigned int seq;
     bool global;
 
-    static int
-    cmp (const void *p1, const void *p2)
-    {
-      const feature_info_t *a = reinterpret_cast<const feature_info_t *>(p1);
-      const feature_info_t *b = reinterpret_cast<const feature_info_t *>(p2);
-
-      if (a->tag != b->tag)
-        return a->tag < b->tag ? -1 : 1;
-
-      return a->seq < b->seq ? -1 : 1;
-    }
+    static int cmp (const feature_info_t *a, const feature_info_t *b)
+    { return (a->tag != b->tag) ?  (a->tag < b->tag ? -1 : 1) : (a->seq < b->seq ? -1 : 1); }
   };
 
   struct feature_map_t {
@@ -68,28 +61,16 @@ struct hb_mask_allocator_t {
     unsigned int shift;
     hb_mask_t mask;
 
-    static int
-    cmp (const void *p1, const void *p2)
-    {
-      const feature_map_t *a = reinterpret_cast<const feature_map_t *>(p1);
-      const feature_map_t *b = reinterpret_cast<const feature_map_t *>(p2);
-
-      return a->tag < b->tag ? -1 : a->tag > b->tag ? 1 : 0;
-    }
+    static int cmp (const feature_map_t *a, const feature_map_t *b)
+    { return a->tag < b->tag ? -1 : a->tag > b->tag ? 1 : 0; }
   };
 
   struct lookup_map_t {
     unsigned int index;
     hb_mask_t mask;
 
-    static int
-    cmp (const void *p1, const void *p2)
-    {
-      const lookup_map_t *a = reinterpret_cast<const lookup_map_t *>(p1);
-      const lookup_map_t *b = reinterpret_cast<const lookup_map_t *>(p2);
-
-      return a->index < b->index ? -1 : a->index > b->index ? 1 : 0;
-    }
+    static int cmp (const lookup_map_t *a, const lookup_map_t *b)
+    { return a->index < b->index ? -1 : a->index > b->index ? 1 : 0; }
   };
 
 
@@ -120,6 +101,7 @@ struct hb_mask_allocator_t {
 
 
 
+  public:
 
   hb_mask_allocator_t (void) : feature_count (0) {}
 
@@ -160,10 +142,8 @@ struct hb_mask_allocator_t {
     }
 
 
-    /* Sort the features so we can bsearch later */
-    qsort (feature_infos, feature_count, sizeof (feature_infos[0]), feature_info_t::cmp);
-
-    /* Remove dups, let later-occurring features override earlier ones. */
+    /* Sort features and merge duplicates */
+    qsort (feature_infos, feature_count, sizeof (feature_infos[0]), (hb_compare_func_t) feature_info_t::cmp);
     unsigned int j = 0;
     for (unsigned int i = 1; i < feature_count; i++)
       if (feature_infos[i].tag != feature_infos[j].tag)
@@ -248,9 +228,7 @@ struct hb_mask_allocator_t {
 	add_lookups (c, table_index, feature_maps[i].index[table_index], feature_maps[i].mask);
 
       /* Sort lookups and merge duplicates */
-
-      qsort (lookup_maps[table_index], lookup_count[table_index], sizeof (lookup_maps[table_index][0]), lookup_map_t::cmp);
-
+      qsort (lookup_maps[table_index], lookup_count[table_index], sizeof (lookup_maps[table_index][0]), (hb_compare_func_t) lookup_map_t::cmp);
       if (lookup_count[table_index])
       {
 	unsigned int j = 0;
@@ -268,22 +246,17 @@ struct hb_mask_allocator_t {
   hb_mask_t get_global_mask (void) { return global_mask; }
 
   hb_mask_t get_mask (hb_tag_t tag, unsigned int *shift) const {
-    const feature_map_t *map = (const feature_map_t *) bsearch (&tag, feature_maps, feature_count, sizeof (feature_maps[0]), feature_map_t::cmp);
-    if (likely (map)) {
-      if (shift) *shift = map->shift;
-      return map->mask;
-    } else {
-      if (shift) *shift = 0;
-      return 0;
-    }
+    const feature_map_t *map = (const feature_map_t *) bsearch (&tag, feature_maps, feature_count, sizeof (feature_maps[0]), (hb_compare_func_t) feature_map_t::cmp);
+    if (shift) *shift = map ? map->shift : 0;
+    return map ? map->mask : 0;
   }
 
-  void substitute (hb_ot_shape_context_t *c) const {
+  inline void substitute (hb_ot_shape_context_t *c) const {
     for (unsigned int i = 0; i < lookup_count[0]; i++)
       hb_ot_layout_substitute_lookup (c->face, c->buffer, lookup_maps[0][i].index, lookup_maps[0][i].mask);
   }
 
-  void position (hb_ot_shape_context_t *c) const {
+  inline void position (hb_ot_shape_context_t *c) const {
     for (unsigned int i = 0; i < lookup_count[1]; i++)
       hb_ot_layout_position_lookup (c->font, c->face, c->buffer, lookup_maps[1][i].index, lookup_maps[1][i].mask);
   }
diff --git a/src/hb-ot-tag.c b/src/hb-ot-tag.c
index 448f795..32d455d 100644
--- a/src/hb-ot-tag.c
+++ b/src/hb-ot-tag.c
@@ -591,10 +591,9 @@ static const LangTag ot_languages[] = {
 };
 
 static int
-lang_compare_first_component (const void *pa,
-			      const void *pb)
+lang_compare_first_component (const char *a,
+			      const char *b)
 {
-  const char *a = pa, *b = pb;
   unsigned int da, db;
   const char *p;
 
@@ -645,7 +644,7 @@ hb_ot_tag_from_language (hb_language_t language)
   /* find a language matching in the first component */
   lang_tag = bsearch (lang_str, ot_languages,
 		      ARRAY_LENGTH (ot_languages), sizeof (LangTag),
-		      lang_compare_first_component);
+		      (hb_compare_func_t) lang_compare_first_component);
 
   /* we now need to find the best language matching */
   if (lang_tag)
@@ -654,12 +653,12 @@ hb_ot_tag_from_language (hb_language_t language)
 
     /* go to the final one matching in the first component */
     while (lang_tag + 1 < ot_languages + ARRAY_LENGTH (ot_languages) &&
-	   lang_compare_first_component (lang_str, lang_tag + 1) == 0)
+	   lang_compare_first_component (lang_str, (lang_tag + 1)->language) == 0)
       lang_tag++;
 
     /* go back, find which one matches completely */
     while (lang_tag >= ot_languages &&
-	   lang_compare_first_component (lang_str, lang_tag) == 0)
+	   lang_compare_first_component (lang_str, lang_tag->language) == 0)
     {
       if (lang_matches (lang_str, lang_tag->language)) {
 	found = TRUE;
diff --git a/src/hb-private.h b/src/hb-private.h
index ad7c362..1d4cfc9 100644
--- a/src/hb-private.h
+++ b/src/hb-private.h
@@ -186,6 +186,10 @@ _hb_ctz (unsigned int number)
 #endif
 }
 
+/* Type of bsearch() / qsort() compare function */
+typedef int (*hb_compare_func_t) (const void *, const void *);
+
+
 /* We need external help for these */
 
 #ifdef HAVE_GLIB
commit a806762a314e83154793d96ee665e6668d6b56de
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Fri Oct 8 19:18:40 2010 -0400

    Add hb-ot-shape-private.hh

diff --git a/src/Makefile.am b/src/Makefile.am
index dba3bb3..c47a1a5 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -49,6 +49,7 @@ HBSOURCES += \
 	hb-ot-layout-private.hh \
 	hb-ot-map-private.hh \
 	hb-ot-shape.cc \
+	hb-ot-shape-private.hh \
 	hb-ot-shape-arabic.cc \
 	hb-ot-tag.c \
 	$(NULL)
diff --git a/src/hb-ot-shape-arabic.cc b/src/hb-ot-shape-arabic.cc
index 1eb3081..7729be1 100644
--- a/src/hb-ot-shape-arabic.cc
+++ b/src/hb-ot-shape-arabic.cc
@@ -24,7 +24,7 @@
  * Google Author(s): Behdad Esfahbod
  */
 
-#include "hb-ot-shape-private.h"
+#include "hb-ot-shape-private.hh"
 
 HB_BEGIN_DECLS
 
diff --git a/src/hb-ot-shape-private.hh b/src/hb-ot-shape-private.hh
new file mode 100644
index 0000000..c8a0295
--- /dev/null
+++ b/src/hb-ot-shape-private.hh
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2010  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_SHAPE_PRIVATE_HH
+#define HB_OT_SHAPE_PRIVATE_HH
+
+#include "hb-private.h"
+
+#include "hb-ot-shape.h"
+
+#include "hb-buffer-private.hh"
+
+HB_BEGIN_DECLS
+
+struct hb_mask_allocator_t;
+
+struct hb_ot_shape_context_t
+{
+  /* Input to hb_ot_shape() */
+  hb_font_t *font;
+  hb_face_t *face;
+  hb_buffer_t  *buffer;
+  hb_feature_t *features;
+  unsigned int  num_features;
+
+  /* Transient stuff */
+  hb_direction_t original_direction;
+  hb_bool_t applied_substitute_complex;
+  hb_bool_t applied_position_complex;
+
+  struct hb_mask_allocator_t *allocator;
+};
+
+
+HB_END_DECLS
+
+#endif /* HB_OT_SHAPE_PRIVATE_HH */
commit 4924affe0f0adf75f2a0e2137a71206b0576d63f
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Fri Oct 8 19:18:16 2010 -0400

    Add hb-ot-map-private.hh

diff --git a/src/Makefile.am b/src/Makefile.am
index 8e7ac47..dba3bb3 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -47,6 +47,7 @@ HBSOURCES += \
 	hb-ot-layout-gsubgpos-private.hh \
 	hb-ot-layout-gsub-private.hh \
 	hb-ot-layout-private.hh \
+	hb-ot-map-private.hh \
 	hb-ot-shape.cc \
 	hb-ot-shape-arabic.cc \
 	hb-ot-tag.c \
diff --git a/src/hb-ot-map-private.hh b/src/hb-ot-map-private.hh
new file mode 100644
index 0000000..786a0d0
--- /dev/null
+++ b/src/hb-ot-map-private.hh
@@ -0,0 +1,307 @@
+/*
+ * Copyright (C) 2009,2010  Red Hat, Inc.
+ * Copyright (C) 2010  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.
+ *
+ * Red Hat Author(s): Behdad Esfahbod
+ * Google Author(s): Behdad Esfahbod
+ */
+
+#ifndef HB_OT_MAP_PRIVATE_HH
+#define HB_OT_MAP_PRIVATE_HH
+
+#include "hb-ot-shape-private.hh"
+
+#include "hb-ot-layout.h"
+
+HB_BEGIN_DECLS
+
+
+#define MAX_FEATURES 100 /* FIXME */
+#define MAX_LOOKUPS 1000 /* FIXME */
+
+static const hb_tag_t table_tags[2] = {HB_OT_TAG_GSUB, HB_OT_TAG_GPOS};
+
+struct hb_mask_allocator_t {
+
+  struct feature_info_t {
+    hb_tag_t tag;
+    unsigned int value;
+    unsigned int seq;
+    bool global;
+
+    static int
+    cmp (const void *p1, const void *p2)
+    {
+      const feature_info_t *a = reinterpret_cast<const feature_info_t *>(p1);
+      const feature_info_t *b = reinterpret_cast<const feature_info_t *>(p2);
+
+      if (a->tag != b->tag)
+        return a->tag < b->tag ? -1 : 1;
+
+      return a->seq < b->seq ? -1 : 1;
+    }
+  };
+
+  struct feature_map_t {
+    hb_tag_t tag; /* should be first for our bsearch to work */
+    unsigned int index[2]; /* GSUB, GPOS */
+    unsigned int shift;
+    hb_mask_t mask;
+
+    static int
+    cmp (const void *p1, const void *p2)
+    {
+      const feature_map_t *a = reinterpret_cast<const feature_map_t *>(p1);
+      const feature_map_t *b = reinterpret_cast<const feature_map_t *>(p2);
+
+      return a->tag < b->tag ? -1 : a->tag > b->tag ? 1 : 0;
+    }
+  };
+
+  struct lookup_map_t {
+    unsigned int index;
+    hb_mask_t mask;
+
+    static int
+    cmp (const void *p1, const void *p2)
+    {
+      const lookup_map_t *a = reinterpret_cast<const lookup_map_t *>(p1);
+      const lookup_map_t *b = reinterpret_cast<const lookup_map_t *>(p2);
+
+      return a->index < b->index ? -1 : a->index > b->index ? 1 : 0;
+    }
+  };
+
+
+  void
+  add_lookups (hb_ot_shape_context_t *c,
+	       unsigned int  table_index,
+	       unsigned int  feature_index,
+	       hb_mask_t     mask)
+  {
+    unsigned int i = MAX_LOOKUPS - lookup_count[table_index];
+    lookup_map_t *lookups = lookup_maps[table_index] + lookup_count[table_index];
+
+    unsigned int *lookup_indices = (unsigned int *) lookups;
+
+    hb_ot_layout_feature_get_lookup_indexes (c->face,
+					     table_tags[table_index],
+					     feature_index,
+					     0, &i,
+					     lookup_indices);
+
+    lookup_count[table_index] += i;
+
+    while (i--) {
+      lookups[i].mask = mask;
+      lookups[i].index = lookup_indices[i];
+    }
+  }
+
+
+
+
+  hb_mask_allocator_t (void) : feature_count (0) {}
+
+  void add_feature (hb_tag_t tag,
+		    unsigned int value,
+		    bool global)
+  {
+    feature_info_t *info = &feature_infos[feature_count++];
+    info->tag = tag;
+    info->value = value;
+    info->seq = feature_count;
+    info->global = global;
+  }
+
+  void compile (hb_ot_shape_context_t *c)
+  {
+   global_mask = 0;
+   lookup_count[0] = lookup_count[1] = 0;
+
+    if (!feature_count)
+      return;
+
+
+    /* Fetch script/language indices for GSUB/GPOS.  We need these later to skip
+     * features not available in either table and not waste precious bits for them. */
+
+    const hb_tag_t *script_tags;
+    hb_tag_t language_tag;
+
+    script_tags = hb_ot_tags_from_script (c->buffer->props.script);
+    language_tag = hb_ot_tag_from_language (c->buffer->props.language);
+
+    unsigned int script_index[2], language_index[2];
+    for (unsigned int table_index = 0; table_index < 2; table_index++) {
+      hb_tag_t table_tag = table_tags[table_index];
+      hb_ot_layout_table_choose_script (c->face, table_tag, script_tags, &script_index[table_index]);
+      hb_ot_layout_script_find_language (c->face, table_tag, script_index[table_index], language_tag, &language_index[table_index]);
+    }
+
+
+    /* Sort the features so we can bsearch later */
+    qsort (feature_infos, feature_count, sizeof (feature_infos[0]), feature_info_t::cmp);
+
+    /* Remove dups, let later-occurring features override earlier ones. */
+    unsigned int j = 0;
+    for (unsigned int i = 1; i < feature_count; i++)
+      if (feature_infos[i].tag != feature_infos[j].tag)
+	feature_infos[++j] = feature_infos[i];
+      else {
+	if (feature_infos[i].global)
+	  feature_infos[j] = feature_infos[i];
+	else {
+	  feature_infos[j].global = feature_infos[j].global && (feature_infos[j].value == feature_infos[i].value);
+	  feature_infos[j].value = MAX (feature_infos[j].value, feature_infos[i].value);
+	}
+      }
+    feature_count = j + 1;
+
+
+    /* Allocate bits now */
+    unsigned int next_bit = 1;
+    j = 0;
+    for (unsigned int i = 0; i < feature_count; i++) {
+      const feature_info_t *info = &feature_infos[i];
+
+      unsigned int bits_needed;
+
+      if (info->global && info->value == 1)
+        /* Uses the global bit */
+        bits_needed = 0;
+      else
+        bits_needed = _hb_bit_storage (info->value);
+
+      if (!info->value || next_bit + bits_needed > 8 * sizeof (hb_mask_t))
+        continue; /* Feature disabled, or not enough bits. */
+
+
+      bool found = false;
+      unsigned int feature_index[2];
+      for (unsigned int table_index = 0; table_index < 2; table_index++)
+        found |= hb_ot_layout_language_find_feature (c->face,
+						     table_tags[table_index],
+						     script_index[table_index],
+						     language_index[table_index],
+						     info->tag,
+						     &feature_index[table_index]);
+      if (!found)
+        continue;
+
+
+      feature_map_t *map = &feature_maps[j++];
+
+      map->tag = info->tag;
+      map->index[0] = feature_index[0];
+      map->index[1] = feature_index[1];
+      if (info->global && info->value == 1) {
+        /* Uses the global bit */
+        map->shift = 0;
+	map->mask = 1;
+      } else {
+	map->shift = next_bit;
+	map->mask = (1 << (next_bit + bits_needed)) - (1 << next_bit);
+	next_bit += bits_needed;
+	if (info->global)
+	  global_mask |= map->mask;
+      }
+
+    }
+    feature_count = j;
+
+
+    for (unsigned int table_index = 0; table_index < 2; table_index++) {
+      hb_tag_t table_tag = table_tags[table_index];
+
+      /* Collect lookup indices for features */
+
+      unsigned int required_feature_index;
+      if (hb_ot_layout_language_get_required_feature_index (c->face,
+							    table_tag,
+							    script_index[table_index],
+							    language_index[table_index],
+							    &required_feature_index))
+	add_lookups (c, table_index, required_feature_index, 1);
+
+      for (unsigned i = 0; i < feature_count; i++)
+	add_lookups (c, table_index, feature_maps[i].index[table_index], feature_maps[i].mask);
+
+      /* Sort lookups and merge duplicates */
+
+      qsort (lookup_maps[table_index], lookup_count[table_index], sizeof (lookup_maps[table_index][0]), lookup_map_t::cmp);
+
+      if (lookup_count[table_index])
+      {
+	unsigned int j = 0;
+	for (unsigned int i = 1; i < lookup_count[table_index]; i++)
+	  if (lookup_maps[table_index][i].index != lookup_maps[table_index][j].index)
+	    lookup_maps[table_index][++j] = lookup_maps[table_index][i];
+	  else
+	    lookup_maps[table_index][j].mask |= lookup_maps[table_index][i].mask;
+	j++;
+	lookup_count[table_index] = j;
+      }
+    }
+  }
+
+  hb_mask_t get_global_mask (void) { return global_mask; }
+
+  hb_mask_t get_mask (hb_tag_t tag, unsigned int *shift) const {
+    const feature_map_t *map = (const feature_map_t *) bsearch (&tag, feature_maps, feature_count, sizeof (feature_maps[0]), feature_map_t::cmp);
+    if (likely (map)) {
+      if (shift) *shift = map->shift;
+      return map->mask;
+    } else {
+      if (shift) *shift = 0;
+      return 0;
+    }
+  }
+
+  void substitute (hb_ot_shape_context_t *c) const {
+    for (unsigned int i = 0; i < lookup_count[0]; i++)
+      hb_ot_layout_substitute_lookup (c->face, c->buffer, lookup_maps[0][i].index, lookup_maps[0][i].mask);
+  }
+
+  void position (hb_ot_shape_context_t *c) const {
+    for (unsigned int i = 0; i < lookup_count[1]; i++)
+      hb_ot_layout_position_lookup (c->font, c->face, c->buffer, lookup_maps[1][i].index, lookup_maps[1][i].mask);
+  }
+
+  private:
+
+  hb_mask_t global_mask;
+
+  unsigned int feature_count;
+  feature_info_t feature_infos[MAX_FEATURES]; /* used before compile() only */
+  feature_map_t feature_maps[MAX_FEATURES];
+
+  lookup_map_t lookup_maps[2][MAX_LOOKUPS]; /* GSUB/GPOS */
+  unsigned int lookup_count[2];
+};
+
+
+
+HB_END_DECLS
+
+#endif /* HB_OT_MAP_PRIVATE_HH */
diff --git a/src/hb-ot-shape.cc b/src/hb-ot-shape.cc
index 11dcaa1..aff1476 100644
--- a/src/hb-ot-shape.cc
+++ b/src/hb-ot-shape.cc
@@ -26,11 +26,9 @@
  * Google Author(s): Behdad Esfahbod
  */
 
-#include "hb-ot-shape-private.h"
+#include "hb-ot-shape-private.hh"
 
-#include "hb-open-type-private.hh"
-
-#include "hb-ot-layout.h"
+#include "hb-ot-map-private.hh"
 
 HB_BEGIN_DECLS
 
@@ -50,266 +48,6 @@ hb_tag_t default_features[] = {
   HB_TAG('r','l','i','g')
 };
 
-#define MAX_FEATURES 100 /* FIXME */
-#define MAX_LOOKUPS 1000 /* FIXME */
-
-static const hb_tag_t table_tags[2] = {HB_OT_TAG_GSUB, HB_OT_TAG_GPOS};
-
-
-struct hb_mask_allocator_t {
-
-  struct feature_info_t {
-    hb_tag_t tag;
-    unsigned int value;
-    unsigned int seq;
-    bool global;
-
-    static int
-    cmp (const void *p1, const void *p2)
-    {
-      const feature_info_t *a = reinterpret_cast<const feature_info_t *>(p1);
-      const feature_info_t *b = reinterpret_cast<const feature_info_t *>(p2);
-
-      if (a->tag != b->tag)
-        return a->tag < b->tag ? -1 : 1;
-
-      return a->seq < b->seq ? -1 : 1;
-    }
-  };
-
-  struct feature_map_t {
-    hb_tag_t tag; /* should be first for our bsearch to work */
-    unsigned int index[2]; /* GSUB, GPOS */
-    unsigned int shift;
-    hb_mask_t mask;
-
-    static int
-    cmp (const void *p1, const void *p2)
-    {
-      const feature_map_t *a = reinterpret_cast<const feature_map_t *>(p1);
-      const feature_map_t *b = reinterpret_cast<const feature_map_t *>(p2);
-
-      return a->tag < b->tag ? -1 : a->tag > b->tag ? 1 : 0;
-    }
-  };
-
-  struct lookup_map_t {
-    unsigned int index;
-    hb_mask_t mask;
-
-    static int
-    cmp (const void *p1, const void *p2)
-    {
-      const lookup_map_t *a = reinterpret_cast<const lookup_map_t *>(p1);
-      const lookup_map_t *b = reinterpret_cast<const lookup_map_t *>(p2);
-
-      return a->index < b->index ? -1 : a->index > b->index ? 1 : 0;
-    }
-  };
-
-
-  void
-  add_lookups (hb_ot_shape_context_t *c,
-	       unsigned int  table_index,
-	       unsigned int  feature_index,
-	       hb_mask_t     mask)
-  {
-    unsigned int i = MAX_LOOKUPS - lookup_count[table_index];
-    lookup_map_t *lookups = lookup_maps[table_index] + lookup_count[table_index];
-
-    unsigned int *lookup_indices = (unsigned int *) lookups;
-
-    hb_ot_layout_feature_get_lookup_indexes (c->face,
-					     table_tags[table_index],
-					     feature_index,
-					     0, &i,
-					     lookup_indices);
-
-    lookup_count[table_index] += i;
-
-    while (i--) {
-      lookups[i].mask = mask;
-      lookups[i].index = lookup_indices[i];
-    }
-  }
-
-
-
-
-  hb_mask_allocator_t (void) : feature_count (0) {}
-
-  void add_feature (hb_tag_t tag,
-		    unsigned int value,
-		    bool global)
-  {
-    feature_info_t *info = &feature_infos[feature_count++];
-    info->tag = tag;
-    info->value = value;
-    info->seq = feature_count;
-    info->global = global;
-  }
-
-  void compile (hb_ot_shape_context_t *c)
-  {
-   global_mask = 0;
-   lookup_count[0] = lookup_count[1] = 0;
-
-    if (!feature_count)
-      return;
-
-
-    /* Fetch script/language indices for GSUB/GPOS.  We need these later to skip
-     * features not available in either table and not waste precious bits for them. */
-
-    const hb_tag_t *script_tags;
-    hb_tag_t language_tag;
-
-    script_tags = hb_ot_tags_from_script (c->buffer->props.script);
-    language_tag = hb_ot_tag_from_language (c->buffer->props.language);
-
-    unsigned int script_index[2], language_index[2];
-    for (unsigned int table_index = 0; table_index < 2; table_index++) {
-      hb_tag_t table_tag = table_tags[table_index];
-      hb_ot_layout_table_choose_script (c->face, table_tag, script_tags, &script_index[table_index]);
-      hb_ot_layout_script_find_language (c->face, table_tag, script_index[table_index], language_tag, &language_index[table_index]);
-    }
-
-
-    /* Sort the features so we can bsearch later */
-    qsort (feature_infos, feature_count, sizeof (feature_infos[0]), feature_info_t::cmp);
-
-    /* Remove dups, let later-occurring features override earlier ones. */
-    unsigned int j = 0;
-    for (unsigned int i = 1; i < feature_count; i++)
-      if (feature_infos[i].tag != feature_infos[j].tag)
-	feature_infos[++j] = feature_infos[i];
-      else {
-	if (feature_infos[i].global)
-	  feature_infos[j] = feature_infos[i];
-	else {
-	  feature_infos[j].global = feature_infos[j].global && (feature_infos[j].value == feature_infos[i].value);
-	  feature_infos[j].value = MAX (feature_infos[j].value, feature_infos[i].value);
-	}
-      }
-    feature_count = j + 1;
-
-
-    /* Allocate bits now */
-    unsigned int next_bit = 1;
-    j = 0;
-    for (unsigned int i = 0; i < feature_count; i++) {
-      const feature_info_t *info = &feature_infos[i];
-
-      unsigned int bits_needed;
-
-      if (info->global && info->value == 1)
-        /* Uses the global bit */
-        bits_needed = 0;
-      else
-        bits_needed = _hb_bit_storage (info->value);
-
-      if (!info->value || next_bit + bits_needed > 8 * sizeof (hb_mask_t))
-        continue; /* Feature disabled, or not enough bits. */
-
-
-      bool found = false;
-      unsigned int feature_index[2];
-      for (unsigned int table_index = 0; table_index < 2; table_index++)
-        found |= hb_ot_layout_language_find_feature (c->face,
-						     table_tags[table_index],
-						     script_index[table_index],
-						     language_index[table_index],
-						     info->tag,
-						     &feature_index[table_index]);
-      if (!found)
-        continue;
-
-
-      feature_map_t *map = &feature_maps[j++];
-
-      map->tag = info->tag;
-      map->index[0] = feature_index[0];
-      map->index[1] = feature_index[1];
-      if (info->global && info->value == 1) {
-        /* Uses the global bit */
-        map->shift = 0;
-	map->mask = 1;
-      } else {
-	map->shift = next_bit;
-	map->mask = (1 << (next_bit + bits_needed)) - (1 << next_bit);
-	next_bit += bits_needed;
-	if (info->global)
-	  global_mask |= map->mask;
-      }
-
-    }
-    feature_count = j;
-
-
-    for (unsigned int table_index = 0; table_index < 2; table_index++) {
-      hb_tag_t table_tag = table_tags[table_index];
-
-      /* Collect lookup indices for features */
-
-      unsigned int required_feature_index;
-      if (hb_ot_layout_language_get_required_feature_index (c->face,
-							    table_tag,
-							    script_index[table_index],
-							    language_index[table_index],
-							    &required_feature_index))
-	add_lookups (c, table_index, required_feature_index, 1);
-
-      for (unsigned i = 0; i < feature_count; i++)
-	add_lookups (c, table_index, feature_maps[i].index[table_index], feature_maps[i].mask);
-
-      /* Sort lookups and merge duplicates */
-
-      qsort (lookup_maps[table_index], lookup_count[table_index], sizeof (lookup_maps[table_index][0]), lookup_map_t::cmp);
-
-      if (lookup_count[table_index])
-      {
-	unsigned int j = 0;
-	for (unsigned int i = 1; i < lookup_count[table_index]; i++)
-	  if (lookup_maps[table_index][i].index != lookup_maps[table_index][j].index)
-	    lookup_maps[table_index][++j] = lookup_maps[table_index][i];
-	  else
-	    lookup_maps[table_index][j].mask |= lookup_maps[table_index][i].mask;
-	j++;
-	lookup_count[table_index] = j;
-      }
-    }
-  }
-
-  hb_mask_t get_global_mask (void) { return global_mask; }
-
-  const feature_map_t *find_feature (hb_tag_t tag) const {
-    static const feature_map_t off_map = { HB_TAG_NONE, {Index::NOT_FOUND_INDEX,Index::NOT_FOUND_INDEX}, 0, 0 };
-    const feature_map_t *map = (const feature_map_t *) bsearch (&tag, feature_maps, feature_count, sizeof (feature_maps[0]), feature_map_t::cmp);
-    return map ? map : &off_map;
-  }
-
-  void substitute (hb_ot_shape_context_t *c) const {
-    for (unsigned int i = 0; i < lookup_count[0]; i++)
-      hb_ot_layout_substitute_lookup (c->face, c->buffer, lookup_maps[0][i].index, lookup_maps[0][i].mask);
-  }
-
-  void position (hb_ot_shape_context_t *c) const {
-    for (unsigned int i = 0; i < lookup_count[1]; i++)
-      hb_ot_layout_position_lookup (c->font, c->face, c->buffer, lookup_maps[1][i].index, lookup_maps[1][i].mask);
-  }
-
-  private:
-
-  hb_mask_t global_mask;
-
-  unsigned int feature_count;
-  feature_info_t feature_infos[MAX_FEATURES]; /* used before compile() only */
-  feature_map_t feature_maps[MAX_FEATURES];
-
-  lookup_map_t lookup_maps[2][MAX_LOOKUPS]; /* GSUB/GPOS */
-  unsigned int lookup_count[2];
-};
-
 static void
 hb_ot_shape_collect_features (hb_ot_shape_context_t *c,
 			      hb_mask_allocator_t   *allocator)
@@ -359,9 +97,11 @@ hb_ot_shape_setup_lookups (hb_ot_shape_context_t *c,
   for (unsigned int i = 0; i < c->num_features; i++)
   {
     hb_feature_t *feature = &c->features[i];
-    const hb_mask_allocator_t::feature_map_t *map = allocator->find_feature (feature->tag);
-    if (!(feature->start == 0 && feature->end == (unsigned int)-1))
-      c->buffer->set_masks (feature->value << map->shift, map->mask, feature->start, feature->end);
+    if (!(feature->start == 0 && feature->end == (unsigned int)-1)) {
+      unsigned int shift;
+      hb_mask_t mask = allocator->get_mask (feature->tag, &shift);
+      c->buffer->set_masks (feature->value << shift, mask, feature->start, feature->end);
+    }
   }
 
   /* complex */
commit a7c5046d6b676a32298b97403a49235f7f255161
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Fri Oct 8 18:47:47 2010 -0400

    Add private hb_segment_properties_t

diff --git a/src/hb-buffer-private.hh b/src/hb-buffer-private.hh
index ce7a3b5..d9f3d4d 100644
--- a/src/hb-buffer-private.hh
+++ b/src/hb-buffer-private.hh
@@ -62,6 +62,12 @@ ASSERT_STATIC (sizeof (hb_glyph_info_t) == sizeof (hb_internal_glyph_info_t));
 ASSERT_STATIC (sizeof (hb_glyph_position_t) == sizeof (hb_internal_glyph_position_t));
 ASSERT_STATIC (sizeof (hb_glyph_info_t) == sizeof (hb_glyph_position_t));
 
+typedef struct _hb_segment_properties_t {
+    hb_direction_t      direction;
+    hb_script_t         script;
+    hb_language_t       language;
+} hb_segment_properties_t;
+
 
 HB_INTERNAL void
 _hb_buffer_swap (hb_buffer_t *buffer);
@@ -110,13 +116,9 @@ struct _hb_buffer_t {
   hb_reference_count_t ref_count;
 
   /* Information about how the text in the buffer should be treated */
-  hb_unicode_funcs_t *unicode;
 
-  struct properties_t {
-    hb_direction_t      direction;
-    hb_script_t         script;
-    hb_language_t       language;
-  } props;
+  hb_unicode_funcs_t *unicode; /* Unicode functions */
+  hb_segment_properties_t props; /* Script, language, direction */
 
   /* Buffer contents */
 
commit b897c607d91d569f4eaa681d1f5b3d9f3d2bb093
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Fri Oct 8 18:41:57 2010 -0400

    Flip the OT bit-allocation vs gsub/gpos inside-out
    
    We now build our entire attack plan before jumping in.

diff --git a/src/hb-ot-shape.cc b/src/hb-ot-shape.cc
index febac35..11dcaa1 100644
--- a/src/hb-ot-shape.cc
+++ b/src/hb-ot-shape.cc
@@ -50,49 +50,11 @@ hb_tag_t default_features[] = {
   HB_TAG('r','l','i','g')
 };
 
-struct lookup_map {
-  unsigned int index;
-  hb_mask_t mask;
-};
-
-
-static void
-add_lookups (hb_face_t    *face,
-	     hb_tag_t      table_tag,
-	     unsigned int  feature_index,
-	     hb_mask_t     mask,
-	     lookup_map   *lookups,
-	     unsigned int *num_lookups,
-	     unsigned int  room_lookups)
-{
-  unsigned int i = room_lookups - *num_lookups;
-  lookups += *num_lookups;
-
-  unsigned int *lookup_indices = (unsigned int *) lookups;
-
-  hb_ot_layout_feature_get_lookup_indexes (face, table_tag, feature_index, 0,
-					   &i,
-					   lookup_indices);
-
-  *num_lookups += i;
-
-  while (i--) {
-    lookups[i].mask = mask;
-    lookups[i].index = lookup_indices[i];
-  }
-}
-
-static int
-cmp_lookups (const void *p1, const void *p2)
-{
-  const lookup_map *a = (const lookup_map *) p1;
-  const lookup_map *b = (const lookup_map *) p2;
-
-  return a->index - b->index;
-}
+#define MAX_FEATURES 100 /* FIXME */
+#define MAX_LOOKUPS 1000 /* FIXME */
 
+static const hb_tag_t table_tags[2] = {HB_OT_TAG_GSUB, HB_OT_TAG_GPOS};
 
-#define MAX_FEATURES 100 /* FIXME */
 
 struct hb_mask_allocator_t {
 
@@ -116,8 +78,8 @@ struct hb_mask_allocator_t {
   };
 
   struct feature_map_t {
-    hb_tag_t tag; /* should be first */
-    unsigned int index;
+    hb_tag_t tag; /* should be first for our bsearch to work */
+    unsigned int index[2]; /* GSUB, GPOS */
     unsigned int shift;
     hb_mask_t mask;
 
@@ -131,50 +93,112 @@ struct hb_mask_allocator_t {
     }
   };
 
-  hb_mask_allocator_t (void) : count (0) {}
+  struct lookup_map_t {
+    unsigned int index;
+    hb_mask_t mask;
+
+    static int
+    cmp (const void *p1, const void *p2)
+    {
+      const lookup_map_t *a = reinterpret_cast<const lookup_map_t *>(p1);
+      const lookup_map_t *b = reinterpret_cast<const lookup_map_t *>(p2);
+
+      return a->index < b->index ? -1 : a->index > b->index ? 1 : 0;
+    }
+  };
+
+
+  void
+  add_lookups (hb_ot_shape_context_t *c,
+	       unsigned int  table_index,
+	       unsigned int  feature_index,
+	       hb_mask_t     mask)
+  {
+    unsigned int i = MAX_LOOKUPS - lookup_count[table_index];
+    lookup_map_t *lookups = lookup_maps[table_index] + lookup_count[table_index];
+
+    unsigned int *lookup_indices = (unsigned int *) lookups;
+
+    hb_ot_layout_feature_get_lookup_indexes (c->face,
+					     table_tags[table_index],
+					     feature_index,
+					     0, &i,
+					     lookup_indices);
+
+    lookup_count[table_index] += i;
+
+    while (i--) {
+      lookups[i].mask = mask;
+      lookups[i].index = lookup_indices[i];
+    }
+  }
+
+
+
+
+  hb_mask_allocator_t (void) : feature_count (0) {}
 
   void add_feature (hb_tag_t tag,
 		    unsigned int value,
 		    bool global)
   {
-    feature_info_t *info = &infos[count++];
+    feature_info_t *info = &feature_infos[feature_count++];
     info->tag = tag;
     info->value = value;
-    info->seq = count;
+    info->seq = feature_count;
     info->global = global;
   }
 
-  void compile (hb_face_t *face,
-		hb_tag_t table_tag,
-		unsigned int script_index,
-		unsigned int language_index)
+  void compile (hb_ot_shape_context_t *c)
   {
-    global_mask = 0;
-    unsigned int next_bit = 1;
+   global_mask = 0;
+   lookup_count[0] = lookup_count[1] = 0;
 
-    if (!count)
+    if (!feature_count)
       return;
 
-    qsort (infos, count, sizeof (infos[0]), feature_info_t::cmp);
 
+    /* Fetch script/language indices for GSUB/GPOS.  We need these later to skip
+     * features not available in either table and not waste precious bits for them. */
+
+    const hb_tag_t *script_tags;
+    hb_tag_t language_tag;
+
+    script_tags = hb_ot_tags_from_script (c->buffer->props.script);
+    language_tag = hb_ot_tag_from_language (c->buffer->props.language);
+
+    unsigned int script_index[2], language_index[2];
+    for (unsigned int table_index = 0; table_index < 2; table_index++) {
+      hb_tag_t table_tag = table_tags[table_index];
+      hb_ot_layout_table_choose_script (c->face, table_tag, script_tags, &script_index[table_index]);
+      hb_ot_layout_script_find_language (c->face, table_tag, script_index[table_index], language_tag, &language_index[table_index]);
+    }
+
+
+    /* Sort the features so we can bsearch later */
+    qsort (feature_infos, feature_count, sizeof (feature_infos[0]), feature_info_t::cmp);
+
+    /* Remove dups, let later-occurring features override earlier ones. */
     unsigned int j = 0;
-    for (unsigned int i = 1; i < count; i++)
-      if (infos[i].tag != infos[j].tag)
-	infos[++j] = infos[i];
+    for (unsigned int i = 1; i < feature_count; i++)
+      if (feature_infos[i].tag != feature_infos[j].tag)
+	feature_infos[++j] = feature_infos[i];
       else {
-	if (infos[i].global)
-	  infos[j] = infos[i];
+	if (feature_infos[i].global)
+	  feature_infos[j] = feature_infos[i];
 	else {
-	  infos[j].global = infos[j].global && (infos[j].value == infos[i].value);
-	  infos[j].value = MAX (infos[j].value, infos[i].value);
+	  feature_infos[j].global = feature_infos[j].global && (feature_infos[j].value == feature_infos[i].value);
+	  feature_infos[j].value = MAX (feature_infos[j].value, feature_infos[i].value);
 	}
       }
-    count = j + 1;
+    feature_count = j + 1;
+
 
     /* Allocate bits now */
+    unsigned int next_bit = 1;
     j = 0;
-    for (unsigned int i = 0; i < count; i++) {
-      const feature_info_t *info = &infos[i];
+    for (unsigned int i = 0; i < feature_count; i++) {
+      const feature_info_t *info = &feature_infos[i];
 
       unsigned int bits_needed;
 
@@ -187,53 +211,103 @@ struct hb_mask_allocator_t {
       if (!info->value || next_bit + bits_needed > 8 * sizeof (hb_mask_t))
         continue; /* Feature disabled, or not enough bits. */
 
-      unsigned int feature_index;
-      if (!hb_ot_layout_language_find_feature (face, table_tag, script_index, language_index,
-					       info->tag, &feature_index))
+
+      bool found = false;
+      unsigned int feature_index[2];
+      for (unsigned int table_index = 0; table_index < 2; table_index++)
+        found |= hb_ot_layout_language_find_feature (c->face,
+						     table_tags[table_index],
+						     script_index[table_index],
+						     language_index[table_index],
+						     info->tag,
+						     &feature_index[table_index]);
+      if (!found)
         continue;
 
-      feature_map_t *map = &maps[j++];
+
+      feature_map_t *map = &feature_maps[j++];
 
       map->tag = info->tag;
-      map->index = feature_index;
+      map->index[0] = feature_index[0];
+      map->index[1] = feature_index[1];
       if (info->global && info->value == 1) {
-        /* Use the global bit */
+        /* Uses the global bit */
         map->shift = 0;
 	map->mask = 1;
       } else {
 	map->shift = next_bit;
 	map->mask = (1 << (next_bit + bits_needed)) - (1 << next_bit);
 	next_bit += bits_needed;
+	if (info->global)
+	  global_mask |= map->mask;
       }
 
-      if (info->global && map->mask != 1)
-        global_mask |= map->mask;
     }
-    count = j;
-  }
+    feature_count = j;
 
-  hb_mask_t get_global_mask (void) { return global_mask; }
 
-  const feature_map_t *get_features (unsigned int *num_features) const {
-    *num_features = count;
-    return maps;
+    for (unsigned int table_index = 0; table_index < 2; table_index++) {
+      hb_tag_t table_tag = table_tags[table_index];
+
+      /* Collect lookup indices for features */
+
+      unsigned int required_feature_index;
+      if (hb_ot_layout_language_get_required_feature_index (c->face,
+							    table_tag,
+							    script_index[table_index],
+							    language_index[table_index],
+							    &required_feature_index))
+	add_lookups (c, table_index, required_feature_index, 1);
+
+      for (unsigned i = 0; i < feature_count; i++)
+	add_lookups (c, table_index, feature_maps[i].index[table_index], feature_maps[i].mask);
+
+      /* Sort lookups and merge duplicates */
+
+      qsort (lookup_maps[table_index], lookup_count[table_index], sizeof (lookup_maps[table_index][0]), lookup_map_t::cmp);
+
+      if (lookup_count[table_index])
+      {
+	unsigned int j = 0;
+	for (unsigned int i = 1; i < lookup_count[table_index]; i++)
+	  if (lookup_maps[table_index][i].index != lookup_maps[table_index][j].index)
+	    lookup_maps[table_index][++j] = lookup_maps[table_index][i];
+	  else
+	    lookup_maps[table_index][j].mask |= lookup_maps[table_index][i].mask;
+	j++;
+	lookup_count[table_index] = j;
+      }
+    }
   }
 
+  hb_mask_t get_global_mask (void) { return global_mask; }
 
   const feature_map_t *find_feature (hb_tag_t tag) const {
-    static const feature_map_t off_map = { HB_TAG_NONE, Index::NOT_FOUND_INDEX, 0, 0 };
-    const feature_map_t *map = (const feature_map_t *) bsearch (&tag, maps, count, sizeof (maps[0]), feature_map_t::cmp);
+    static const feature_map_t off_map = { HB_TAG_NONE, {Index::NOT_FOUND_INDEX,Index::NOT_FOUND_INDEX}, 0, 0 };
+    const feature_map_t *map = (const feature_map_t *) bsearch (&tag, feature_maps, feature_count, sizeof (feature_maps[0]), feature_map_t::cmp);
     return map ? map : &off_map;
   }
 
+  void substitute (hb_ot_shape_context_t *c) const {
+    for (unsigned int i = 0; i < lookup_count[0]; i++)
+      hb_ot_layout_substitute_lookup (c->face, c->buffer, lookup_maps[0][i].index, lookup_maps[0][i].mask);
+  }
 
-  private:
+  void position (hb_ot_shape_context_t *c) const {
+    for (unsigned int i = 0; i < lookup_count[1]; i++)
+      hb_ot_layout_position_lookup (c->font, c->face, c->buffer, lookup_maps[1][i].index, lookup_maps[1][i].mask);
+  }
 
-  unsigned int count;
-  feature_info_t infos[MAX_FEATURES];
+  private:
 
-  feature_map_t maps[MAX_FEATURES];
   hb_mask_t global_mask;
+
+  unsigned int feature_count;
+  feature_info_t feature_infos[MAX_FEATURES]; /* used before compile() only */
+  feature_map_t feature_maps[MAX_FEATURES];
+
+  lookup_map_t lookup_maps[2][MAX_LOOKUPS]; /* GSUB/GPOS */
+  unsigned int lookup_count[2];
 };
 
 static void
@@ -269,70 +343,23 @@ hb_ot_shape_collect_features (hb_ot_shape_context_t *c,
 
 static void
 hb_ot_shape_setup_lookups (hb_ot_shape_context_t *c,
-			   lookup_map            *lookups,
-			   unsigned int          *num_lookups)
+			   hb_mask_allocator_t   *allocator)
 {
-  hb_mask_allocator_t allocator;
-
-  hb_ot_shape_collect_features (c, &allocator);
+  hb_ot_shape_collect_features (c, allocator);
 
   /* Compile features */
-  unsigned int script_index, language_index, feature_index;
-  unsigned int room_lookups;
-
-  hb_ot_layout_table_choose_script (c->face, c->table_tag,
-				    hb_ot_tags_from_script (c->buffer->props.script),
-				    &script_index);
-  hb_ot_layout_script_find_language (c->face, c->table_tag, script_index,
-				     hb_ot_tag_from_language (c->buffer->props.language),
-				     &language_index);
-
-  allocator.compile (c->face, c->table_tag, script_index, language_index);
-
-
-  /* Gather lookup indices for features */
-
-  room_lookups = *num_lookups;
-  *num_lookups = 0;
-
-  if (hb_ot_layout_language_get_required_feature_index (c->face, c->table_tag, script_index, language_index,
-							&feature_index))
-    add_lookups (c->face, c->table_tag, feature_index, 1, lookups, num_lookups, room_lookups);
-
-  const hb_mask_allocator_t::feature_map_t *map;
-  unsigned int num_features;
-
-  map = allocator.get_features (&num_features);
-  for (unsigned i = 0; i < num_features; i++)
-    add_lookups (c->face, c->table_tag, map[i].index, map[i].mask, lookups, num_lookups, room_lookups);
-
-  /* Sort lookups and merge duplicates */
-
-  qsort (lookups, *num_lookups, sizeof (lookups[0]), cmp_lookups);
-
-  if (*num_lookups)
-  {
-    unsigned int j = 0;
-    for (unsigned int i = 1; i < *num_lookups; i++)
-      if (lookups[i].index != lookups[j].index)
-	lookups[++j] = lookups[i];
-      else
-        lookups[j].mask |= lookups[i].mask;
-    j++;
-    *num_lookups = j;
-  }
-
+  allocator->compile (c);
 
   /* Set masks in buffer */
 
-  hb_mask_t global_mask = allocator.get_global_mask ();
+  hb_mask_t global_mask = allocator->get_global_mask ();
   if (global_mask)
     c->buffer->set_masks (global_mask, global_mask, 0, (unsigned int) -1);
 
   for (unsigned int i = 0; i < c->num_features; i++)
   {
     hb_feature_t *feature = &c->features[i];
-    map = allocator.find_feature (feature->tag);
+    const hb_mask_allocator_t::feature_map_t *map = allocator->find_feature (feature->tag);
     if (!(feature->start == 0 && feature->end == (unsigned int)-1))
       c->buffer->set_masks (feature->value << map->shift, map->mask, feature->start, feature->end);
   }
@@ -342,40 +369,27 @@ hb_ot_shape_setup_lookups (hb_ot_shape_context_t *c,
 
 
 static void
-hb_ot_substitute_complex (hb_ot_shape_context_t *c)
+hb_ot_substitute_complex (hb_ot_shape_context_t *c,
+			  const hb_mask_allocator_t *allocator)
 {
-  lookup_map lookups[1000]; /* FIXME */
-  unsigned int num_lookups = ARRAY_LENGTH (lookups);
-
   if (!hb_ot_layout_has_substitution (c->face))
     return;
 
-  c->table_tag = HB_OT_TAG_GSUB;
-
-  hb_ot_shape_setup_lookups (c, lookups, &num_lookups);
-
-  for (unsigned int i = 0; i < num_lookups; i++)
-    hb_ot_layout_substitute_lookup (c->face, c->buffer, lookups[i].index, lookups[i].mask);
+  allocator->substitute (c);
 
   c->applied_substitute_complex = TRUE;
   return;
 }
 
 static void
-hb_ot_position_complex (hb_ot_shape_context_t *c)
+hb_ot_position_complex (hb_ot_shape_context_t *c,
+			const hb_mask_allocator_t *allocator)
 {
-  lookup_map lookups[1000]; /* FIXME */
-  unsigned int num_lookups = ARRAY_LENGTH (lookups);
 
   if (!hb_ot_layout_has_positioning (c->face))
     return;
 
-  c->table_tag = HB_OT_TAG_GPOS;
-
-  hb_ot_shape_setup_lookups (c, lookups, &num_lookups);
-
-  for (unsigned int i = 0; i < num_lookups; i++)
-    hb_ot_layout_position_lookup (c->font, c->face, c->buffer, lookups[i].index, lookups[i].mask);
+  allocator->position (c);
 
   hb_ot_layout_position_finish (c->font, c->face, c->buffer);
 
@@ -528,6 +542,11 @@ hb_position_complex_fallback_visual (hb_ot_shape_context_t *c)
 static void
 hb_ot_shape_internal (hb_ot_shape_context_t *c)
 {
+  hb_mask_allocator_t allocator;
+
+  hb_ot_shape_setup_lookups (c, &allocator);
+
+
   hb_form_clusters (c->buffer);
 
   /* SUBSTITUTE */
@@ -541,7 +560,7 @@ hb_ot_shape_internal (hb_ot_shape_context_t *c)
 
     hb_substitute_default (c);
 
-    hb_ot_substitute_complex (c);
+    hb_ot_substitute_complex (c, &allocator);
 
     if (!c->applied_substitute_complex)
       hb_substitute_complex_fallback (c);
@@ -553,7 +572,7 @@ hb_ot_shape_internal (hb_ot_shape_context_t *c)
 
     hb_position_default (c);
 
-    hb_ot_position_complex (c);
+    hb_ot_position_complex (c, &allocator);
 
     hb_bool_t position_fallback = !c->applied_position_complex;
     if (position_fallback)
commit e89b7d2a61b7f58e6c7cec00d5ce2246dee1e8a1
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Fri Oct 8 12:29:59 2010 -0400

    Logically separate feature collection

diff --git a/src/hb-ot-shape.cc b/src/hb-ot-shape.cc
index 8b8b76f..febac35 100644
--- a/src/hb-ot-shape.cc
+++ b/src/hb-ot-shape.cc
@@ -237,20 +237,17 @@ struct hb_mask_allocator_t {
 };
 
 static void
-hb_ot_shape_setup_lookups (hb_ot_shape_context_t *c,
-			   lookup_map            *lookups,
-			   unsigned int          *num_lookups)
+hb_ot_shape_collect_features (hb_ot_shape_context_t *c,
+			      hb_mask_allocator_t   *allocator)
 {
-  hb_mask_allocator_t allocator;
-
   switch (c->original_direction) {
     case HB_DIRECTION_LTR:
-      allocator.add_feature (HB_TAG ('l','t','r','a'), 1, true);
-      allocator.add_feature (HB_TAG ('l','t','r','m'), 1, true);
+      allocator->add_feature (HB_TAG ('l','t','r','a'), 1, true);
+      allocator->add_feature (HB_TAG ('l','t','r','m'), 1, true);
       break;
     case HB_DIRECTION_RTL:
-      allocator.add_feature (HB_TAG ('r','t','l','a'), 1, true);
-      allocator.add_feature (HB_TAG ('r','t','l','m'), 1, false);
+      allocator->add_feature (HB_TAG ('r','t','l','a'), 1, true);
+      allocator->add_feature (HB_TAG ('r','t','l','m'), 1, false);
       break;
     case HB_DIRECTION_TTB:
     case HB_DIRECTION_BTT:
@@ -259,15 +256,25 @@ hb_ot_shape_setup_lookups (hb_ot_shape_context_t *c,
   }
 
   for (unsigned int i = 0; i < ARRAY_LENGTH (default_features); i++)
-    allocator.add_feature (default_features[i], 1, true);
+    allocator->add_feature (default_features[i], 1, true);
 
   /* complex */
 
   for (unsigned int i = 0; i < c->num_features; i++) {
     const hb_feature_t *feature = &c->features[i];
-    allocator.add_feature (feature->tag, feature->value, (feature->start == 0 && feature->end == (unsigned int) -1));
+    allocator->add_feature (feature->tag, feature->value, (feature->start == 0 && feature->end == (unsigned int) -1));
   }
+}
+
+
+static void
+hb_ot_shape_setup_lookups (hb_ot_shape_context_t *c,
+			   lookup_map            *lookups,
+			   unsigned int          *num_lookups)
+{
+  hb_mask_allocator_t allocator;
 
+  hb_ot_shape_collect_features (c, &allocator);
 
   /* Compile features */
   unsigned int script_index, language_index, feature_index;
commit 5b88908f12ad1d828dd6075fb8fc0036c2d6af3a
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Fri Oct 8 12:23:01 2010 -0400

    Minor

diff --git a/src/hb-ot-shape.cc b/src/hb-ot-shape.cc
index a206ff9..8b8b76f 100644
--- a/src/hb-ot-shape.cc
+++ b/src/hb-ot-shape.cc
@@ -237,14 +237,6 @@ struct hb_mask_allocator_t {
 };
 
 static void
-hb_ot_shape_setup_lookups_complex (hb_ot_shape_context_t *c,
-				   lookup_map            *lookups,
-				   unsigned int          *num_lookups)
-{
-  /* XXX */
-}
-
-static void
 hb_ot_shape_setup_lookups (hb_ot_shape_context_t *c,
 			   lookup_map            *lookups,
 			   unsigned int          *num_lookups)
@@ -269,7 +261,7 @@ hb_ot_shape_setup_lookups (hb_ot_shape_context_t *c,
   for (unsigned int i = 0; i < ARRAY_LENGTH (default_features); i++)
     allocator.add_feature (default_features[i], 1, true);
 
-  hb_ot_shape_setup_lookups_complex (c, lookups, num_lookups);
+  /* complex */
 
   for (unsigned int i = 0; i < c->num_features; i++) {
     const hb_feature_t *feature = &c->features[i];
@@ -326,6 +318,10 @@ hb_ot_shape_setup_lookups (hb_ot_shape_context_t *c,
 
   /* Set masks in buffer */
 
+  hb_mask_t global_mask = allocator.get_global_mask ();
+  if (global_mask)
+    c->buffer->set_masks (global_mask, global_mask, 0, (unsigned int) -1);
+
   for (unsigned int i = 0; i < c->num_features; i++)
   {
     hb_feature_t *feature = &c->features[i];
@@ -334,9 +330,7 @@ hb_ot_shape_setup_lookups (hb_ot_shape_context_t *c,
       c->buffer->set_masks (feature->value << map->shift, map->mask, feature->start, feature->end);
   }
 
-  hb_mask_t global_mask = allocator.get_global_mask ();
-  if (global_mask)
-    c->buffer->set_masks (global_mask, global_mask, 0, (unsigned int) -1);
+  /* complex */
 }
 
 
commit 5360ce0c5c33f921b3f9ad3f42529a19df5ad0fe
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Thu Oct 7 21:21:11 2010 -0400

    Move some more code around

diff --git a/src/hb-ot-shape.cc b/src/hb-ot-shape.cc
index dd7a546..a206ff9 100644
--- a/src/hb-ot-shape.cc
+++ b/src/hb-ot-shape.cc
@@ -249,20 +249,6 @@ hb_ot_shape_setup_lookups (hb_ot_shape_context_t *c,
 			   lookup_map            *lookups,
 			   unsigned int          *num_lookups)
 {
-  unsigned int script_index, language_index, feature_index;
-  unsigned int room_lookups;
-
-  room_lookups = *num_lookups;
-  *num_lookups = 0;
-
-  hb_ot_layout_table_choose_script (c->face, c->table_tag,
-				    hb_ot_tags_from_script (c->buffer->props.script),
-				    &script_index);
-  hb_ot_layout_script_find_language (c->face, c->table_tag, script_index,
-				     hb_ot_tag_from_language (c->buffer->props.language),
-				     &language_index);
-
-
   hb_mask_allocator_t allocator;
 
   switch (c->original_direction) {
@@ -292,11 +278,24 @@ hb_ot_shape_setup_lookups (hb_ot_shape_context_t *c,
 
 
   /* Compile features */
+  unsigned int script_index, language_index, feature_index;
+  unsigned int room_lookups;
+
+  hb_ot_layout_table_choose_script (c->face, c->table_tag,
+				    hb_ot_tags_from_script (c->buffer->props.script),
+				    &script_index);
+  hb_ot_layout_script_find_language (c->face, c->table_tag, script_index,
+				     hb_ot_tag_from_language (c->buffer->props.language),
+				     &language_index);
+
   allocator.compile (c->face, c->table_tag, script_index, language_index);
 
 
   /* Gather lookup indices for features */
 
+  room_lookups = *num_lookups;
+  *num_lookups = 0;
+
   if (hb_ot_layout_language_get_required_feature_index (c->face, c->table_tag, script_index, language_index,
 							&feature_index))
     add_lookups (c->face, c->table_tag, feature_index, 1, lookups, num_lookups, room_lookups);
commit d9c726078828d50db62e05407a3f38f2e7607533
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Thu Oct 7 21:19:54 2010 -0400

    Minor

diff --git a/src/hb-ot-shape.cc b/src/hb-ot-shape.cc
index 58d1e7f..dd7a546 100644
--- a/src/hb-ot-shape.cc
+++ b/src/hb-ot-shape.cc
@@ -249,7 +249,8 @@ hb_ot_shape_setup_lookups (hb_ot_shape_context_t *c,
 			   lookup_map            *lookups,
 			   unsigned int          *num_lookups)
 {
-  unsigned int i, j, script_index, language_index, feature_index, room_lookups;
+  unsigned int script_index, language_index, feature_index;
+  unsigned int room_lookups;
 
   room_lookups = *num_lookups;
   *num_lookups = 0;
@@ -279,7 +280,7 @@ hb_ot_shape_setup_lookups (hb_ot_shape_context_t *c,
       break;
   }
 
-  for (i = 0; i < ARRAY_LENGTH (default_features); i++)
+  for (unsigned int i = 0; i < ARRAY_LENGTH (default_features); i++)
     allocator.add_feature (default_features[i], 1, true);
 
   hb_ot_shape_setup_lookups_complex (c, lookups, num_lookups);
@@ -304,7 +305,7 @@ hb_ot_shape_setup_lookups (hb_ot_shape_context_t *c,
   unsigned int num_features;
 
   map = allocator.get_features (&num_features);
-  for (i = 0; i < num_features; i++)
+  for (unsigned i = 0; i < num_features; i++)
     add_lookups (c->face, c->table_tag, map[i].index, map[i].mask, lookups, num_lookups, room_lookups);
 
   /* Sort lookups and merge duplicates */
@@ -313,7 +314,8 @@ hb_ot_shape_setup_lookups (hb_ot_shape_context_t *c,
 
   if (*num_lookups)
   {
-    for (i = 1, j = 0; i < *num_lookups; i++)
+    unsigned int j = 0;
+    for (unsigned int i = 1; i < *num_lookups; i++)
       if (lookups[i].index != lookups[j].index)
 	lookups[++j] = lookups[i];
       else
@@ -325,7 +327,7 @@ hb_ot_shape_setup_lookups (hb_ot_shape_context_t *c,
 
   /* Set masks in buffer */
 
-  for (i = 0; i < c->num_features; i++)
+  for (unsigned int i = 0; i < c->num_features; i++)
   {
     hb_feature_t *feature = &c->features[i];
     map = allocator.find_feature (feature->tag);
@@ -344,7 +346,6 @@ hb_ot_substitute_complex (hb_ot_shape_context_t *c)
 {
   lookup_map lookups[1000]; /* FIXME */
   unsigned int num_lookups = ARRAY_LENGTH (lookups);
-  unsigned int i;
 
   if (!hb_ot_layout_has_substitution (c->face))
     return;
@@ -353,7 +354,7 @@ hb_ot_substitute_complex (hb_ot_shape_context_t *c)
 
   hb_ot_shape_setup_lookups (c, lookups, &num_lookups);
 
-  for (i = 0; i < num_lookups; i++)
+  for (unsigned int i = 0; i < num_lookups; i++)
     hb_ot_layout_substitute_lookup (c->face, c->buffer, lookups[i].index, lookups[i].mask);
 
   c->applied_substitute_complex = TRUE;
@@ -365,7 +366,6 @@ hb_ot_position_complex (hb_ot_shape_context_t *c)
 {
   lookup_map lookups[1000]; /* FIXME */
   unsigned int num_lookups = ARRAY_LENGTH (lookups);
-  unsigned int i;
 
   if (!hb_ot_layout_has_positioning (c->face))
     return;
@@ -374,7 +374,7 @@ hb_ot_position_complex (hb_ot_shape_context_t *c)
 
   hb_ot_shape_setup_lookups (c, lookups, &num_lookups);
 
-  for (i = 0; i < num_lookups; i++)
+  for (unsigned int i = 0; i < num_lookups; i++)
     hb_ot_layout_position_lookup (c->font, c->face, c->buffer, lookups[i].index, lookups[i].mask);
 
   hb_ot_layout_position_finish (c->font, c->face, c->buffer);
commit efe0d682e860ffd23a1d17c68c8273f17d51c1c9
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Thu Oct 7 21:12:46 2010 -0400

    Simplify compiling lookups

diff --git a/src/hb-ot-shape.cc b/src/hb-ot-shape.cc
index 79f3097..58d1e7f 100644
--- a/src/hb-ot-shape.cc
+++ b/src/hb-ot-shape.cc
@@ -213,6 +213,13 @@ struct hb_mask_allocator_t {
   }
 
   hb_mask_t get_global_mask (void) { return global_mask; }
+
+  const feature_map_t *get_features (unsigned int *num_features) const {
+    *num_features = count;
+    return maps;
+  }
+
+
   const feature_map_t *find_feature (hb_tag_t tag) const {
     static const feature_map_t off_map = { HB_TAG_NONE, Index::NOT_FOUND_INDEX, 0, 0 };
     const feature_map_t *map = (const feature_map_t *) bsearch (&tag, maps, count, sizeof (maps[0]), feature_map_t::cmp);
@@ -287,52 +294,18 @@ hb_ot_shape_setup_lookups (hb_ot_shape_context_t *c,
   allocator.compile (c->face, c->table_tag, script_index, language_index);
 
 
-  /* Gather lookup indices for features and set buffer masks at the same time */
+  /* Gather lookup indices for features */
 
   if (hb_ot_layout_language_get_required_feature_index (c->face, c->table_tag, script_index, language_index,
 							&feature_index))
     add_lookups (c->face, c->table_tag, feature_index, 1, lookups, num_lookups, room_lookups);
 
   const hb_mask_allocator_t::feature_map_t *map;
+  unsigned int num_features;
 
-  hb_mask_t global_mask = allocator.get_global_mask ();
-  if (global_mask)
-    c->buffer->set_masks (global_mask, global_mask, 0, (unsigned int) -1);
-
-  switch (c->original_direction) {
-    case HB_DIRECTION_LTR:
-      map = allocator.find_feature (HB_TAG ('l','t','r','a'));
-      add_lookups (c->face, c->table_tag, map->index, map->mask, lookups, num_lookups, room_lookups);
-      map = allocator.find_feature (HB_TAG ('l','t','r','m'));
-      add_lookups (c->face, c->table_tag, map->index, map->mask, lookups, num_lookups, room_lookups);
-      break;
-    case HB_DIRECTION_RTL:
-      map = allocator.find_feature (HB_TAG ('r','t','l','a'));
-      add_lookups (c->face, c->table_tag, map->index, map->mask, lookups, num_lookups, room_lookups);
-      map = allocator.find_feature (HB_TAG ('r','t','l','m'));
-      add_lookups (c->face, c->table_tag, map->index, map->mask, lookups, num_lookups, room_lookups);
-      break;
-    case HB_DIRECTION_TTB:
-    case HB_DIRECTION_BTT:
-    default:
-      break;
-  }
-
-  for (i = 0; i < ARRAY_LENGTH (default_features); i++)
-  {
-    map = allocator.find_feature (default_features[i]);
-    add_lookups (c->face, c->table_tag, map->index, map->mask, lookups, num_lookups, room_lookups);
-  }
-
-  for (i = 0; i < c->num_features; i++)
-  {
-    hb_feature_t *feature = &c->features[i];
-    map = allocator.find_feature (feature->tag);
-    add_lookups (c->face, c->table_tag, map->index, map->mask, lookups, num_lookups, room_lookups);
-    if (!(feature->start == 0 && feature->end == (unsigned int)-1))
-      c->buffer->set_masks (feature->value << map->shift, map->mask, feature->start, feature->end);
-  }
-
+  map = allocator.get_features (&num_features);
+  for (i = 0; i < num_features; i++)
+    add_lookups (c->face, c->table_tag, map[i].index, map[i].mask, lookups, num_lookups, room_lookups);
 
   /* Sort lookups and merge duplicates */
 
@@ -348,6 +321,21 @@ hb_ot_shape_setup_lookups (hb_ot_shape_context_t *c,
     j++;
     *num_lookups = j;
   }
+
+
+  /* Set masks in buffer */
+
+  for (i = 0; i < c->num_features; i++)
+  {
+    hb_feature_t *feature = &c->features[i];
+    map = allocator.find_feature (feature->tag);
+    if (!(feature->start == 0 && feature->end == (unsigned int)-1))
+      c->buffer->set_masks (feature->value << map->shift, map->mask, feature->start, feature->end);
+  }
+
+  hb_mask_t global_mask = allocator.get_global_mask ();
+  if (global_mask)
+    c->buffer->set_masks (global_mask, global_mask, 0, (unsigned int) -1);
 }
 
 
commit 476c94218b4f5b8e119e82b0e10b641e0c10bf56
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Thu Oct 7 17:47:33 2010 -0400

    Rename

diff --git a/src/hb-ot-shape.cc b/src/hb-ot-shape.cc
index a4eafe1..79f3097 100644
--- a/src/hb-ot-shape.cc
+++ b/src/hb-ot-shape.cc
@@ -1,5 +1,6 @@
 /*
  * Copyright (C) 2009,2010  Red Hat, Inc.
+ * Copyright (C) 2010  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
  */
 
 #include "hb-ot-shape-private.h"
@@ -55,7 +57,7 @@ struct lookup_map {
 
 
 static void
-add_feature (hb_face_t    *face,
+add_lookups (hb_face_t    *face,
 	     hb_tag_t      table_tag,
 	     unsigned int  feature_index,
 	     hb_mask_t     mask,
@@ -289,7 +291,7 @@ hb_ot_shape_setup_lookups (hb_ot_shape_context_t *c,
 
   if (hb_ot_layout_language_get_required_feature_index (c->face, c->table_tag, script_index, language_index,
 							&feature_index))
-    add_feature (c->face, c->table_tag, feature_index, 1, lookups, num_lookups, room_lookups);
+    add_lookups (c->face, c->table_tag, feature_index, 1, lookups, num_lookups, room_lookups);
 
   const hb_mask_allocator_t::feature_map_t *map;
 
@@ -300,15 +302,15 @@ hb_ot_shape_setup_lookups (hb_ot_shape_context_t *c,
   switch (c->original_direction) {
     case HB_DIRECTION_LTR:
       map = allocator.find_feature (HB_TAG ('l','t','r','a'));
-      add_feature (c->face, c->table_tag, map->index, map->mask, lookups, num_lookups, room_lookups);
+      add_lookups (c->face, c->table_tag, map->index, map->mask, lookups, num_lookups, room_lookups);
       map = allocator.find_feature (HB_TAG ('l','t','r','m'));
-      add_feature (c->face, c->table_tag, map->index, map->mask, lookups, num_lookups, room_lookups);
+      add_lookups (c->face, c->table_tag, map->index, map->mask, lookups, num_lookups, room_lookups);
       break;
     case HB_DIRECTION_RTL:
       map = allocator.find_feature (HB_TAG ('r','t','l','a'));
-      add_feature (c->face, c->table_tag, map->index, map->mask, lookups, num_lookups, room_lookups);
+      add_lookups (c->face, c->table_tag, map->index, map->mask, lookups, num_lookups, room_lookups);
       map = allocator.find_feature (HB_TAG ('r','t','l','m'));
-      add_feature (c->face, c->table_tag, map->index, map->mask, lookups, num_lookups, room_lookups);
+      add_lookups (c->face, c->table_tag, map->index, map->mask, lookups, num_lookups, room_lookups);
       break;
     case HB_DIRECTION_TTB:
     case HB_DIRECTION_BTT:
@@ -319,14 +321,14 @@ hb_ot_shape_setup_lookups (hb_ot_shape_context_t *c,
   for (i = 0; i < ARRAY_LENGTH (default_features); i++)
   {
     map = allocator.find_feature (default_features[i]);
-    add_feature (c->face, c->table_tag, map->index, map->mask, lookups, num_lookups, room_lookups);
+    add_lookups (c->face, c->table_tag, map->index, map->mask, lookups, num_lookups, room_lookups);
   }
 
   for (i = 0; i < c->num_features; i++)
   {
     hb_feature_t *feature = &c->features[i];
     map = allocator.find_feature (feature->tag);
-    add_feature (c->face, c->table_tag, map->index, map->mask, lookups, num_lookups, room_lookups);
+    add_lookups (c->face, c->table_tag, map->index, map->mask, lookups, num_lookups, room_lookups);
     if (!(feature->start == 0 && feature->end == (unsigned int)-1))
       c->buffer->set_masks (feature->value << map->shift, map->mask, feature->start, feature->end);
   }
commit 34db6f031d7ac009f554386ef990bad44886b9ee
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Thu Oct 7 01:21:19 2010 -0400

    Add XXX note

diff --git a/src/hb-buffer.cc b/src/hb-buffer.cc
index ccc075a..047dafd 100644
--- a/src/hb-buffer.cc
+++ b/src/hb-buffer.cc
@@ -483,6 +483,7 @@ _hb_buffer_set_masks (hb_buffer_t *buffer,
     return;
   }
 
+  /* XXX can't bsearch since .cluster may not be sorted. */
   /* Binary search to find the start position and go from there. */
   unsigned int min = 0, max = buffer->len;
   while (min < max)
commit 98aa3f65446496dc250d9b01d98cacfdf1157e06
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Wed Oct 6 00:23:36 2010 -0400

    Call hb_ot_shape_setup_lookups_complex()

diff --git a/src/hb-ot-shape.cc b/src/hb-ot-shape.cc
index 053a823..a4eafe1 100644
--- a/src/hb-ot-shape.cc
+++ b/src/hb-ot-shape.cc
@@ -228,6 +228,14 @@ struct hb_mask_allocator_t {
 };
 
 static void
+hb_ot_shape_setup_lookups_complex (hb_ot_shape_context_t *c,
+				   lookup_map            *lookups,
+				   unsigned int          *num_lookups)
+{
+  /* XXX */
+}
+
+static void
 hb_ot_shape_setup_lookups (hb_ot_shape_context_t *c,
 			   lookup_map            *lookups,
 			   unsigned int          *num_lookups)
@@ -265,7 +273,7 @@ hb_ot_shape_setup_lookups (hb_ot_shape_context_t *c,
   for (i = 0; i < ARRAY_LENGTH (default_features); i++)
     allocator.add_feature (default_features[i], 1, true);
 
-  /* XXX complex-shaper features go here */
+  hb_ot_shape_setup_lookups_complex (c, lookups, num_lookups);
 
   for (unsigned int i = 0; i < c->num_features; i++) {
     const hb_feature_t *feature = &c->features[i];
commit f1d07885dc358e79c237e824c94b3320c0a9c17d
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Wed Oct 6 00:21:37 2010 -0400

    Rename setup_lookups()

diff --git a/src/hb-ot-shape.cc b/src/hb-ot-shape.cc
index d79ccfb..053a823 100644
--- a/src/hb-ot-shape.cc
+++ b/src/hb-ot-shape.cc
@@ -228,9 +228,9 @@ struct hb_mask_allocator_t {
 };
 
 static void
-setup_lookups (hb_ot_shape_context_t *c,
-	       lookup_map            *lookups,
-	       unsigned int          *num_lookups)
+hb_ot_shape_setup_lookups (hb_ot_shape_context_t *c,
+			   lookup_map            *lookups,
+			   unsigned int          *num_lookups)
 {
   unsigned int i, j, script_index, language_index, feature_index, room_lookups;
 
@@ -353,7 +353,7 @@ hb_ot_substitute_complex (hb_ot_shape_context_t *c)
 
   c->table_tag = HB_OT_TAG_GSUB;
 
-  setup_lookups (c, lookups, &num_lookups);
+  hb_ot_shape_setup_lookups (c, lookups, &num_lookups);
 
   for (i = 0; i < num_lookups; i++)
     hb_ot_layout_substitute_lookup (c->face, c->buffer, lookups[i].index, lookups[i].mask);
@@ -374,7 +374,7 @@ hb_ot_position_complex (hb_ot_shape_context_t *c)
 
   c->table_tag = HB_OT_TAG_GPOS;
 
-  setup_lookups (c, lookups, &num_lookups);
+  hb_ot_shape_setup_lookups (c, lookups, &num_lookups);
 
   for (i = 0; i < num_lookups; i++)
     hb_ot_layout_position_lookup (c->font, c->face, c->buffer, lookups[i].index, lookups[i].mask);
commit affc5abac7bdae51df85856a5478d34d96fda4fe
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Wed Oct 6 00:18:16 2010 -0400

    Move table_tag to hb_ot_shape_context_t

diff --git a/src/hb-ot-shape.cc b/src/hb-ot-shape.cc
index 25048a1..d79ccfb 100644
--- a/src/hb-ot-shape.cc
+++ b/src/hb-ot-shape.cc
@@ -229,7 +229,6 @@ struct hb_mask_allocator_t {
 
 static void
 setup_lookups (hb_ot_shape_context_t *c,
-	       hb_tag_t               table_tag,
 	       lookup_map            *lookups,
 	       unsigned int          *num_lookups)
 {
@@ -238,10 +237,10 @@ setup_lookups (hb_ot_shape_context_t *c,
   room_lookups = *num_lookups;
   *num_lookups = 0;
 
-  hb_ot_layout_table_choose_script (c->face, table_tag,
+  hb_ot_layout_table_choose_script (c->face, c->table_tag,
 				    hb_ot_tags_from_script (c->buffer->props.script),
 				    &script_index);
-  hb_ot_layout_script_find_language (c->face, table_tag, script_index,
+  hb_ot_layout_script_find_language (c->face, c->table_tag, script_index,
 				     hb_ot_tag_from_language (c->buffer->props.language),
 				     &language_index);
 
@@ -275,14 +274,14 @@ setup_lookups (hb_ot_shape_context_t *c,
 
 
   /* Compile features */
-  allocator.compile (c->face, table_tag, script_index, language_index);
+  allocator.compile (c->face, c->table_tag, script_index, language_index);
 
 
   /* Gather lookup indices for features and set buffer masks at the same time */
 
-  if (hb_ot_layout_language_get_required_feature_index (c->face, table_tag, script_index, language_index,
+  if (hb_ot_layout_language_get_required_feature_index (c->face, c->table_tag, script_index, language_index,
 							&feature_index))
-    add_feature (c->face, table_tag, feature_index, 1, lookups, num_lookups, room_lookups);
+    add_feature (c->face, c->table_tag, feature_index, 1, lookups, num_lookups, room_lookups);
 
   const hb_mask_allocator_t::feature_map_t *map;
 
@@ -293,15 +292,15 @@ setup_lookups (hb_ot_shape_context_t *c,
   switch (c->original_direction) {
     case HB_DIRECTION_LTR:
       map = allocator.find_feature (HB_TAG ('l','t','r','a'));
-      add_feature (c->face, table_tag, map->index, map->mask, lookups, num_lookups, room_lookups);
+      add_feature (c->face, c->table_tag, map->index, map->mask, lookups, num_lookups, room_lookups);
       map = allocator.find_feature (HB_TAG ('l','t','r','m'));
-      add_feature (c->face, table_tag, map->index, map->mask, lookups, num_lookups, room_lookups);
+      add_feature (c->face, c->table_tag, map->index, map->mask, lookups, num_lookups, room_lookups);
       break;
     case HB_DIRECTION_RTL:
       map = allocator.find_feature (HB_TAG ('r','t','l','a'));
-      add_feature (c->face, table_tag, map->index, map->mask, lookups, num_lookups, room_lookups);
+      add_feature (c->face, c->table_tag, map->index, map->mask, lookups, num_lookups, room_lookups);
       map = allocator.find_feature (HB_TAG ('r','t','l','m'));
-      add_feature (c->face, table_tag, map->index, map->mask, lookups, num_lookups, room_lookups);
+      add_feature (c->face, c->table_tag, map->index, map->mask, lookups, num_lookups, room_lookups);
       break;
     case HB_DIRECTION_TTB:
     case HB_DIRECTION_BTT:
@@ -312,14 +311,14 @@ setup_lookups (hb_ot_shape_context_t *c,
   for (i = 0; i < ARRAY_LENGTH (default_features); i++)
   {
     map = allocator.find_feature (default_features[i]);
-    add_feature (c->face, table_tag, map->index, map->mask, lookups, num_lookups, room_lookups);
+    add_feature (c->face, c->table_tag, map->index, map->mask, lookups, num_lookups, room_lookups);
   }
 
   for (i = 0; i < c->num_features; i++)
   {
     hb_feature_t *feature = &c->features[i];
     map = allocator.find_feature (feature->tag);
-    add_feature (c->face, table_tag, map->index, map->mask, lookups, num_lookups, room_lookups);
+    add_feature (c->face, c->table_tag, map->index, map->mask, lookups, num_lookups, room_lookups);
     if (!(feature->start == 0 && feature->end == (unsigned int)-1))
       c->buffer->set_masks (feature->value << map->shift, map->mask, feature->start, feature->end);
   }
@@ -352,7 +351,9 @@ hb_ot_substitute_complex (hb_ot_shape_context_t *c)
   if (!hb_ot_layout_has_substitution (c->face))
     return;
 
-  setup_lookups (c, HB_OT_TAG_GSUB, lookups, &num_lookups);
+  c->table_tag = HB_OT_TAG_GSUB;
+
+  setup_lookups (c, lookups, &num_lookups);
 
   for (i = 0; i < num_lookups; i++)
     hb_ot_layout_substitute_lookup (c->face, c->buffer, lookups[i].index, lookups[i].mask);
@@ -371,7 +372,9 @@ hb_ot_position_complex (hb_ot_shape_context_t *c)
   if (!hb_ot_layout_has_positioning (c->face))
     return;
 
-  setup_lookups (c, HB_OT_TAG_GPOS, lookups, &num_lookups);
+  c->table_tag = HB_OT_TAG_GPOS;
+
+  setup_lookups (c, lookups, &num_lookups);
 
   for (i = 0; i < num_lookups; i++)
     hb_ot_layout_position_lookup (c->font, c->face, c->buffer, lookups[i].index, lookups[i].mask);
commit 967240dd8b96802345ef273e75427066e91ea8fb
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Tue Oct 5 23:00:05 2010 -0400

    Add internal hb_ot_shape_context_t

diff --git a/src/hb-ot-shape-arabic.cc b/src/hb-ot-shape-arabic.cc
index 705a6f9..1eb3081 100644
--- a/src/hb-ot-shape-arabic.cc
+++ b/src/hb-ot-shape-arabic.cc
@@ -24,13 +24,7 @@
  * Google Author(s): Behdad Esfahbod
  */
 
-#include "hb-ot-shape.h"
-
-#include "hb-buffer-private.hh"
-
-#include "hb-open-type-private.hh"
-
-#include "hb-ot-layout.h"
+#include "hb-ot-shape-private.h"
 
 HB_BEGIN_DECLS
 
@@ -676,18 +670,14 @@ static const struct arabic_state_table_entry {
 
 
 void
-_hb_ot_analyze_complex_arabic (hb_font_t    *font,
-			       hb_face_t    *face,
-			       hb_buffer_t  *buffer,
-			       hb_feature_t *features HB_UNUSED,
-			       unsigned int  num_features HB_UNUSED)
+_hb_ot_analyze_complex_arabic (hb_ot_shape_context_t *c)
 {
-  unsigned int count = buffer->len;
+  unsigned int count = c->buffer->len;
   unsigned int prev = 0, state = 0;
 
   for (unsigned int i = 0; i < count; i++) {
 
-    unsigned int this_type = get_joining_type (buffer->info[i].codepoint, buffer->unicode->v.get_general_category (buffer->info[i].codepoint));
+    unsigned int this_type = get_joining_type (c->buffer->info[i].codepoint, c->buffer->unicode->v.get_general_category (c->buffer->info[i].codepoint));
 
     if (unlikely (this_type == JOINING_TYPE_T))
       continue;
@@ -695,21 +685,21 @@ _hb_ot_analyze_complex_arabic (hb_font_t    *font,
     const arabic_state_table_entry *entry = arabic_state_table[state];
 
     if (entry->prev_action != NONE)
-      buffer->info[prev].gproperty = entry->prev_action;
+      c->buffer->info[prev].gproperty = entry->prev_action;
 
-    buffer->info[i].gproperty = entry->curr_action;
+    c->buffer->info[i].gproperty = entry->curr_action;
 
     prev = i;
     state = entry->next_state;
   }
 
   hb_mask_t mask_array[TOTAL_NUM_FEATURES] = {0};
-  unsigned int num_masks = buffer->props.script == HB_SCRIPT_SYRIAC ? SYRIAC_NUM_FEATURES : COMMON_NUM_FEATURES;
+  unsigned int num_masks = c->buffer->props.script == HB_SCRIPT_SYRIAC ? SYRIAC_NUM_FEATURES : COMMON_NUM_FEATURES;
   for (unsigned int i = 0; i < num_masks; i++)
     mask_array[i] = 0 /* XXX find_mask */;
 
   for (unsigned int i = 0; i < count; i++)
-    buffer->info[i].mask |= mask_array[buffer->info[i].gproperty];
+    c->buffer->info[i].mask |= mask_array[c->buffer->info[i].gproperty];
 }
 
 
diff --git a/src/hb-ot-shape.cc b/src/hb-ot-shape.cc
index 4173942..25048a1 100644
--- a/src/hb-ot-shape.cc
+++ b/src/hb-ot-shape.cc
@@ -24,9 +24,7 @@
  * Red Hat Author(s): Behdad Esfahbod
  */
 
-#include "hb-ot-shape.h"
-
-#include "hb-buffer-private.hh"
+#include "hb-ot-shape-private.h"
 
 #include "hb-open-type-private.hh"
 
@@ -230,31 +228,27 @@ struct hb_mask_allocator_t {
 };
 
 static void
-setup_lookups (hb_face_t    *face,
-	       hb_buffer_t  *buffer,
-	       hb_feature_t *features,
-	       unsigned int  num_features,
-	       hb_tag_t      table_tag,
-	       lookup_map   *lookups,
-	       unsigned int *num_lookups,
-	       hb_direction_t original_direction)
+setup_lookups (hb_ot_shape_context_t *c,
+	       hb_tag_t               table_tag,
+	       lookup_map            *lookups,
+	       unsigned int          *num_lookups)
 {
   unsigned int i, j, script_index, language_index, feature_index, room_lookups;
 
   room_lookups = *num_lookups;
   *num_lookups = 0;
 
-  hb_ot_layout_table_choose_script (face, table_tag,
-				    hb_ot_tags_from_script (buffer->props.script),
+  hb_ot_layout_table_choose_script (c->face, table_tag,
+				    hb_ot_tags_from_script (c->buffer->props.script),
 				    &script_index);
-  hb_ot_layout_script_find_language (face, table_tag, script_index,
-				     hb_ot_tag_from_language (buffer->props.language),
+  hb_ot_layout_script_find_language (c->face, table_tag, script_index,
+				     hb_ot_tag_from_language (c->buffer->props.language),
 				     &language_index);
 
 
   hb_mask_allocator_t allocator;
 
-  switch (original_direction) {
+  switch (c->original_direction) {
     case HB_DIRECTION_LTR:
       allocator.add_feature (HB_TAG ('l','t','r','a'), 1, true);
       allocator.add_feature (HB_TAG ('l','t','r','m'), 1, true);
@@ -274,40 +268,40 @@ setup_lookups (hb_face_t    *face,
 
   /* XXX complex-shaper features go here */
 
-  for (unsigned int i = 0; i < num_features; i++) {
-    const hb_feature_t *feature = &features[i];
+  for (unsigned int i = 0; i < c->num_features; i++) {
+    const hb_feature_t *feature = &c->features[i];
     allocator.add_feature (feature->tag, feature->value, (feature->start == 0 && feature->end == (unsigned int) -1));
   }
 
 
   /* Compile features */
-  allocator.compile (face, table_tag, script_index, language_index);
+  allocator.compile (c->face, table_tag, script_index, language_index);
 
 
   /* Gather lookup indices for features and set buffer masks at the same time */
 
-  if (hb_ot_layout_language_get_required_feature_index (face, table_tag, script_index, language_index,
+  if (hb_ot_layout_language_get_required_feature_index (c->face, table_tag, script_index, language_index,
 							&feature_index))
-    add_feature (face, table_tag, feature_index, 1, lookups, num_lookups, room_lookups);
+    add_feature (c->face, table_tag, feature_index, 1, lookups, num_lookups, room_lookups);
 
   const hb_mask_allocator_t::feature_map_t *map;
 
   hb_mask_t global_mask = allocator.get_global_mask ();
   if (global_mask)
-    buffer->set_masks (global_mask, global_mask, 0, (unsigned int) -1);
+    c->buffer->set_masks (global_mask, global_mask, 0, (unsigned int) -1);
 
-  switch (original_direction) {
+  switch (c->original_direction) {
     case HB_DIRECTION_LTR:
       map = allocator.find_feature (HB_TAG ('l','t','r','a'));
-      add_feature (face, table_tag, map->index, map->mask, lookups, num_lookups, room_lookups);
+      add_feature (c->face, table_tag, map->index, map->mask, lookups, num_lookups, room_lookups);
       map = allocator.find_feature (HB_TAG ('l','t','r','m'));
-      add_feature (face, table_tag, map->index, map->mask, lookups, num_lookups, room_lookups);
+      add_feature (c->face, table_tag, map->index, map->mask, lookups, num_lookups, room_lookups);
       break;
     case HB_DIRECTION_RTL:
       map = allocator.find_feature (HB_TAG ('r','t','l','a'));
-      add_feature (face, table_tag, map->index, map->mask, lookups, num_lookups, room_lookups);
+      add_feature (c->face, table_tag, map->index, map->mask, lookups, num_lookups, room_lookups);
       map = allocator.find_feature (HB_TAG ('r','t','l','m'));
-      add_feature (face, table_tag, map->index, map->mask, lookups, num_lookups, room_lookups);
+      add_feature (c->face, table_tag, map->index, map->mask, lookups, num_lookups, room_lookups);
       break;
     case HB_DIRECTION_TTB:
     case HB_DIRECTION_BTT:
@@ -318,16 +312,16 @@ setup_lookups (hb_face_t    *face,
   for (i = 0; i < ARRAY_LENGTH (default_features); i++)
   {
     map = allocator.find_feature (default_features[i]);
-    add_feature (face, table_tag, map->index, map->mask, lookups, num_lookups, room_lookups);
+    add_feature (c->face, table_tag, map->index, map->mask, lookups, num_lookups, room_lookups);
   }
 
-  for (i = 0; i < num_features; i++)
+  for (i = 0; i < c->num_features; i++)
   {
-    hb_feature_t *feature = &features[i];
+    hb_feature_t *feature = &c->features[i];
     map = allocator.find_feature (feature->tag);
-    add_feature (face, table_tag, map->index, map->mask, lookups, num_lookups, room_lookups);
+    add_feature (c->face, table_tag, map->index, map->mask, lookups, num_lookups, room_lookups);
     if (!(feature->start == 0 && feature->end == (unsigned int)-1))
-      buffer->set_masks (features[i].value << map->shift, map->mask, feature->start, feature->end);
+      c->buffer->set_masks (feature->value << map->shift, map->mask, feature->start, feature->end);
   }
 
 
@@ -348,58 +342,44 @@ setup_lookups (hb_face_t    *face,
 }
 
 
-static hb_bool_t
-hb_ot_substitute_complex (hb_font_t    *font HB_UNUSED,
-			  hb_face_t    *face,
-			  hb_buffer_t  *buffer,
-			  hb_feature_t *features,
-			  unsigned int  num_features,
-			  hb_direction_t original_direction)
+static void
+hb_ot_substitute_complex (hb_ot_shape_context_t *c)
 {
   lookup_map lookups[1000]; /* FIXME */
   unsigned int num_lookups = ARRAY_LENGTH (lookups);
   unsigned int i;
 
-  if (!hb_ot_layout_has_substitution (face))
-    return FALSE;
+  if (!hb_ot_layout_has_substitution (c->face))
+    return;
 
-  setup_lookups (face, buffer, features, num_features,
-		 HB_OT_TAG_GSUB,
-		 lookups, &num_lookups,
-		 original_direction);
+  setup_lookups (c, HB_OT_TAG_GSUB, lookups, &num_lookups);
 
   for (i = 0; i < num_lookups; i++)
-    hb_ot_layout_substitute_lookup (face, buffer, lookups[i].index, lookups[i].mask);
+    hb_ot_layout_substitute_lookup (c->face, c->buffer, lookups[i].index, lookups[i].mask);
 
-  return TRUE;
+  c->applied_substitute_complex = TRUE;
+  return;
 }
 
-static hb_bool_t
-hb_ot_position_complex (hb_font_t    *font,
-			hb_face_t    *face,
-			hb_buffer_t  *buffer,
-			hb_feature_t *features,
-			unsigned int  num_features,
-			hb_direction_t original_direction)
+static void
+hb_ot_position_complex (hb_ot_shape_context_t *c)
 {
-  lookup_map lookups[1000];
+  lookup_map lookups[1000]; /* FIXME */
   unsigned int num_lookups = ARRAY_LENGTH (lookups);
   unsigned int i;
 
-  if (!hb_ot_layout_has_positioning (face))
-    return FALSE;
+  if (!hb_ot_layout_has_positioning (c->face))
+    return;
 
-  setup_lookups (face, buffer, features, num_features,
-		 HB_OT_TAG_GPOS,
-		 lookups, &num_lookups,
-		 original_direction);
+  setup_lookups (c, HB_OT_TAG_GPOS, lookups, &num_lookups);
 
   for (i = 0; i < num_lookups; i++)
-    hb_ot_layout_position_lookup (font, face, buffer, lookups[i].index, lookups[i].mask);
+    hb_ot_layout_position_lookup (c->font, c->face, c->buffer, lookups[i].index, lookups[i].mask);
 
-  hb_ot_layout_position_finish (font, face, buffer);
+  hb_ot_layout_position_finish (c->font, c->face, c->buffer);
 
-  return TRUE;
+  c->applied_position_complex = TRUE;
+  return;
 }
 
 
@@ -424,20 +404,18 @@ hb_form_clusters (hb_buffer_t *buffer)
       buffer->info[i].cluster = buffer->info[i - 1].cluster;
 }
 
-static hb_direction_t
+static void
 hb_ensure_native_direction (hb_buffer_t *buffer)
 {
-  hb_direction_t original_direction = buffer->props.direction;
+  hb_direction_t direction = buffer->props.direction;
 
   /* TODO vertical */
-  if (HB_DIRECTION_IS_HORIZONTAL (original_direction) &&
-      original_direction != _hb_script_get_horizontal_direction (buffer->props.script))
+  if (HB_DIRECTION_IS_HORIZONTAL (direction) &&
+      direction != _hb_script_get_horizontal_direction (buffer->props.script))
   {
     hb_buffer_reverse_clusters (buffer);
     buffer->props.direction = HB_DIRECTION_REVERSE (buffer->props.direction);
   }
-
-  return original_direction;
 }
 
 
@@ -487,21 +465,13 @@ hb_map_glyphs (hb_font_t    *font,
 }
 
 static void
-hb_substitute_default (hb_font_t    *font,
-		       hb_face_t    *face,
-		       hb_buffer_t  *buffer,
-		       hb_feature_t *features HB_UNUSED,
-		       unsigned int  num_features HB_UNUSED)
+hb_substitute_default (hb_ot_shape_context_t *c)
 {
-  hb_map_glyphs (font, face, buffer);
+  hb_map_glyphs (c->font, c->face, c->buffer);
 }
 
 static void
-hb_substitute_complex_fallback (hb_font_t    *font HB_UNUSED,
-				hb_face_t    *face HB_UNUSED,
-				hb_buffer_t  *buffer HB_UNUSED,
-				hb_feature_t *features HB_UNUSED,
-				unsigned int  num_features HB_UNUSED)
+hb_substitute_complex_fallback (hb_ot_shape_context_t *c HB_UNUSED)
 {
   /* TODO Arabic */
 }
@@ -510,114 +480,107 @@ hb_substitute_complex_fallback (hb_font_t    *font HB_UNUSED,
 /* Position */
 
 static void
-hb_position_default (hb_font_t    *font,
-		     hb_face_t    *face,
-		     hb_buffer_t  *buffer,
-		     hb_feature_t *features HB_UNUSED,
-		     unsigned int  num_features HB_UNUSED)
+hb_position_default (hb_ot_shape_context_t *c)
 {
-  hb_buffer_clear_positions (buffer);
+  hb_buffer_clear_positions (c->buffer);
 
-  unsigned int count = buffer->len;
+  unsigned int count = c->buffer->len;
   for (unsigned int i = 0; i < count; i++) {
     hb_glyph_metrics_t metrics;
-    hb_font_get_glyph_metrics (font, face, buffer->info[i].codepoint, &metrics);
-    buffer->pos[i].x_advance = metrics.x_advance;
-    buffer->pos[i].y_advance = metrics.y_advance;
+    hb_font_get_glyph_metrics (c->font, c->face, c->buffer->info[i].codepoint, &metrics);
+    c->buffer->pos[i].x_advance = metrics.x_advance;
+    c->buffer->pos[i].y_advance = metrics.y_advance;
   }
 }
 
 static void
-hb_position_complex_fallback (hb_font_t    *font HB_UNUSED,
-			      hb_face_t    *face HB_UNUSED,
-			      hb_buffer_t  *buffer HB_UNUSED,
-			      hb_feature_t *features HB_UNUSED,
-			      unsigned int  num_features HB_UNUSED)
+hb_position_complex_fallback (hb_ot_shape_context_t *c HB_UNUSED)
 {
   /* TODO Mark pos */
 }
 
 static void
-hb_truetype_kern (hb_font_t    *font,
-		  hb_face_t    *face,
-		  hb_buffer_t  *buffer,
-		  hb_feature_t *features HB_UNUSED,
-		  unsigned int  num_features HB_UNUSED)
+hb_truetype_kern (hb_ot_shape_context_t *c)
 {
   /* TODO Check for kern=0 */
-  unsigned int count = buffer->len;
+  unsigned int count = c->buffer->len;
   for (unsigned int i = 1; i < count; i++) {
     hb_position_t kern, kern1, kern2;
-    kern = hb_font_get_kerning (font, face, buffer->info[i - 1].codepoint, buffer->info[i].codepoint);
+    kern = hb_font_get_kerning (c->font, c->face, c->buffer->info[i - 1].codepoint, c->buffer->info[i].codepoint);
     kern1 = kern >> 1;
     kern2 = kern - kern1;
-    buffer->pos[i - 1].x_advance += kern1;
-    buffer->pos[i].x_advance += kern2;
-    buffer->pos[i].x_offset += kern2;
+    c->buffer->pos[i - 1].x_advance += kern1;
+    c->buffer->pos[i].x_advance += kern2;
+    c->buffer->pos[i].x_offset += kern2;
   }
 }
 
 static void
-hb_position_complex_fallback_visual (hb_font_t    *font,
-				     hb_face_t    *face,
-				     hb_buffer_t  *buffer,
-				     hb_feature_t *features,
-				     unsigned int  num_features)
+hb_position_complex_fallback_visual (hb_ot_shape_context_t *c)
 {
-  hb_truetype_kern (font, face, buffer, features, num_features);
+  hb_truetype_kern (c);
 }
 
 
 /* Do it! */
 
-void
-hb_ot_shape (hb_font_t    *font,
-	     hb_face_t    *face,
-	     hb_buffer_t  *buffer,
-	     hb_feature_t *features,
-	     unsigned int  num_features)
+static void
+hb_ot_shape_internal (hb_ot_shape_context_t *c)
 {
-  hb_direction_t original_direction;
-  hb_bool_t substitute_fallback, position_fallback;
-
-  hb_form_clusters (buffer);
+  hb_form_clusters (c->buffer);
 
   /* SUBSTITUTE */
   {
-    buffer->clear_masks ();
+    c->buffer->clear_masks ();
 
     /* Mirroring needs to see the original direction */
-    hb_mirror_chars (buffer);
+    hb_mirror_chars (c->buffer);
 
-    original_direction = hb_ensure_native_direction (buffer);
+    hb_ensure_native_direction (c->buffer);
 
-    hb_substitute_default (font, face, buffer, features, num_features);
+    hb_substitute_default (c);
 
-    substitute_fallback = !hb_ot_substitute_complex (font, face, buffer, features, num_features, original_direction);
+    hb_ot_substitute_complex (c);
 
-    if (substitute_fallback)
-      hb_substitute_complex_fallback (font, face, buffer, features, num_features);
+    if (!c->applied_substitute_complex)
+      hb_substitute_complex_fallback (c);
   }
 
   /* POSITION */
   {
-    buffer->clear_masks ();
+    c->buffer->clear_masks ();
 
-    hb_position_default (font, face, buffer, features, num_features);
+    hb_position_default (c);
 
-    position_fallback = !hb_ot_position_complex (font, face, buffer, features, num_features, original_direction);
+    hb_ot_position_complex (c);
 
+    hb_bool_t position_fallback = !c->applied_position_complex;
     if (position_fallback)
-      hb_position_complex_fallback (font, face, buffer, features, num_features);
+      hb_position_complex_fallback (c);
 
-    if (HB_DIRECTION_IS_BACKWARD (buffer->props.direction))
-      hb_buffer_reverse (buffer);
+    if (HB_DIRECTION_IS_BACKWARD (c->buffer->props.direction))
+      hb_buffer_reverse (c->buffer);
 
     if (position_fallback)
-      hb_position_complex_fallback_visual (font, face, buffer, features, num_features);
+      hb_position_complex_fallback_visual (c);
   }
 
-  buffer->props.direction = original_direction;
+  c->buffer->props.direction = c->original_direction;
+}
+
+void
+hb_ot_shape (hb_font_t    *font,
+	     hb_face_t    *face,
+	     hb_buffer_t  *buffer,
+	     hb_feature_t *features,
+	     unsigned int  num_features)
+{
+  hb_ot_shape_context_t c = {font, face, buffer, features, num_features};
+
+  /* Setup transient context members */
+  c.original_direction = buffer->props.direction;
+
+  hb_ot_shape_internal (&c);
 }
 
 
commit 3eb936f1539475098f39be78654b9c39b86f0799
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Tue Oct 5 18:36:58 2010 -0400

    Add Arabic/Syriac/N'ko shaping logic
    
    Not hooked up just yet.

diff --git a/src/Makefile.am b/src/Makefile.am
index ea6fb50..8e7ac47 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1,6 +1,7 @@
 # Process this file with automake to produce Makefile.in
 
 NULL =
+EXTRA_DIST = 
 
 # The following warning options are useful for debugging: -Wpadded -Wcast-align
 #AM_CXXFLAGS =
@@ -47,6 +48,7 @@ HBSOURCES += \
 	hb-ot-layout-gsub-private.hh \
 	hb-ot-layout-private.hh \
 	hb-ot-shape.cc \
+	hb-ot-shape-arabic.cc \
 	hb-ot-tag.c \
 	$(NULL)
 HBHEADERS += \
@@ -106,6 +108,13 @@ libharfbuzz_la_CPPFLAGS = $(HBCFLAGS)
 libharfbuzz_la_LIBADD = $(HBLIBS)
 pkginclude_HEADERS = $(HBHEADERS)
 
+
+GENERATORS = \
+	gen-arabic-joining-table.py \
+	$(NULL)
+
+EXTRA_DIST += $(GENERATORS)
+
 noinst_PROGRAMS = main test
 
 main_SOURCES = main.cc
diff --git a/src/gen-arabic-joining-table.py b/src/gen-arabic-joining-table.py
new file mode 100755
index 0000000..f12c207
--- /dev/null
+++ b/src/gen-arabic-joining-table.py
@@ -0,0 +1,39 @@
+#!/usr/bin/python
+
+import sys
+
+header = sys.stdin.readline(), sys.stdin.readline()
+dic = dict()
+for line in sys.stdin:
+	if line[:1] != '0':
+		continue
+
+	fields = [x.strip() for x in line.split(';')]
+	u = int(fields[0], 16)
+
+	if u < 0x0600 or (u > 0x07FF and u != 0x200C and u != 0x200D):
+		raise Exception ("Ooops, unexpected unicode character: ", fields)
+	dic[u] = fields
+
+print "  /*"
+print "   * The following table is generated by running:"
+print "   *"
+print "   *   ./gen-arabic-joining-table.py < ArabicShaping.txt"
+print "   *"
+print "   * on the ArabicShaping.txt file with the header:"
+print "   *"
+for line in header:
+	print "   * %s" % (line.strip())
+print "   */"
+print "  /* == Start of generated table == */"
+for i in range(0x0600, 0x0800):
+	if i not in dic:
+		print "  JOINING_TYPE_X, /* %04X */" % i
+	else:
+		entry = dic[i]
+		if entry[3] in ["ALAPH", "DALATH RISH"]:
+			value = "JOINING_GROUP_" + entry[3].replace(' ', '_')
+		else:
+			value = "JOINING_TYPE_" + entry[2]
+		print "  %s, /* %s */" % (value, '; '.join(entry))
+print "  /* == End of generated table == */"
diff --git a/src/hb-ot-shape-arabic.cc b/src/hb-ot-shape-arabic.cc
new file mode 100644
index 0000000..705a6f9
--- /dev/null
+++ b/src/hb-ot-shape-arabic.cc
@@ -0,0 +1,716 @@
+/*
+ * Copyright (C) 2010  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.h"
+
+#include "hb-buffer-private.hh"
+
+#include "hb-open-type-private.hh"
+
+#include "hb-ot-layout.h"
+
+HB_BEGIN_DECLS
+
+/*
+ * Bits used in the joining tables
+ */
+enum {
+  JOINING_TYPE_U		= 0,
+  JOINING_TYPE_R		= 1,
+  JOINING_TYPE_D		= 2,
+  JOINING_TYPE_C		= JOINING_TYPE_D,
+  JOINING_GROUP_ALAPH		= 3,
+  JOINING_GROUP_DALATH_RISH	= 4,
+  NUM_STATE_MACHINE_COLS	= 5,
+
+  /* We deliberately don't have a JOINING_TYPE_L since that's unused in Unicode. */
+
+  JOINING_TYPE_T = 6,
+  JOINING_TYPE_X = 7, /* means: use general-category to choose between U or T. */
+};
+
+/*
+ * Joining types:
+ */
+
+
+/*
+ * Main joining-type table, covering U+0600..U+07FF.
+ * Includes Arabic, Syriac, and N'ko.
+ */
+static const uint8_t arabic_syriac_nko_joining_types[0x0800 - 0x0600 + 1] =
+{
+  /*
+   * The following table is generated by running:
+   *
+   *   ./gen-arabic-joining-table.py < ArabicShaping.txt
+   *
+   * on the ArabicShaping.txt file with the header:
+   *
+   * # ArabicShaping-5.2.0.txt
+   * # Date: 2009-08-17, 11:11:00 PDT [KW]
+   */
+  /* == Start of generated table == */
+  JOINING_TYPE_U, /* 0600; ARABIC NUMBER SIGN; U; No_Joining_Group */
+  JOINING_TYPE_U, /* 0601; ARABIC SIGN SANAH; U; No_Joining_Group */
+  JOINING_TYPE_U, /* 0602; ARABIC FOOTNOTE MARKER; U; No_Joining_Group */
+  JOINING_TYPE_U, /* 0603; ARABIC SIGN SAFHA; U; No_Joining_Group */
+  JOINING_TYPE_X, /* 0604 */
+  JOINING_TYPE_X, /* 0605 */
+  JOINING_TYPE_X, /* 0606 */
+  JOINING_TYPE_X, /* 0607 */
+  JOINING_TYPE_U, /* 0608; ARABIC RAY; U; No_Joining_Group */
+  JOINING_TYPE_X, /* 0609 */
+  JOINING_TYPE_X, /* 060A */
+  JOINING_TYPE_U, /* 060B; AFGHANI SIGN; U; No_Joining_Group */
+  JOINING_TYPE_X, /* 060C */
+  JOINING_TYPE_X, /* 060D */
+  JOINING_TYPE_X, /* 060E */
+  JOINING_TYPE_X, /* 060F */
+  JOINING_TYPE_X, /* 0610 */
+  JOINING_TYPE_X, /* 0611 */
+  JOINING_TYPE_X, /* 0612 */
+  JOINING_TYPE_X, /* 0613 */
+  JOINING_TYPE_X, /* 0614 */
+  JOINING_TYPE_X, /* 0615 */
+  JOINING_TYPE_X, /* 0616 */
+  JOINING_TYPE_X, /* 0617 */
+  JOINING_TYPE_X, /* 0618 */
+  JOINING_TYPE_X, /* 0619 */
+  JOINING_TYPE_X, /* 061A */
+  JOINING_TYPE_X, /* 061B */
+  JOINING_TYPE_X, /* 061C */
+  JOINING_TYPE_X, /* 061D */
+  JOINING_TYPE_X, /* 061E */
+  JOINING_TYPE_X, /* 061F */
+  JOINING_TYPE_X, /* 0620 */
+  JOINING_TYPE_U, /* 0621; HAMZA; U; No_Joining_Group */
+  JOINING_TYPE_R, /* 0622; MADDA ON ALEF; R; ALEF */
+  JOINING_TYPE_R, /* 0623; HAMZA ON ALEF; R; ALEF */
+  JOINING_TYPE_R, /* 0624; HAMZA ON WAW; R; WAW */
+  JOINING_TYPE_R, /* 0625; HAMZA UNDER ALEF; R; ALEF */
+  JOINING_TYPE_D, /* 0626; HAMZA ON YEH; D; YEH */
+  JOINING_TYPE_R, /* 0627; ALEF; R; ALEF */
+  JOINING_TYPE_D, /* 0628; BEH; D; BEH */
+  JOINING_TYPE_R, /* 0629; TEH MARBUTA; R; TEH MARBUTA */
+  JOINING_TYPE_D, /* 062A; TEH; D; BEH */
+  JOINING_TYPE_D, /* 062B; THEH; D; BEH */
+  JOINING_TYPE_D, /* 062C; JEEM; D; HAH */
+  JOINING_TYPE_D, /* 062D; HAH; D; HAH */
+  JOINING_TYPE_D, /* 062E; KHAH; D; HAH */
+  JOINING_TYPE_R, /* 062F; DAL; R; DAL */
+  JOINING_TYPE_R, /* 0630; THAL; R; DAL */
+  JOINING_TYPE_R, /* 0631; REH; R; REH */
+  JOINING_TYPE_R, /* 0632; ZAIN; R; REH */
+  JOINING_TYPE_D, /* 0633; SEEN; D; SEEN */
+  JOINING_TYPE_D, /* 0634; SHEEN; D; SEEN */
+  JOINING_TYPE_D, /* 0635; SAD; D; SAD */
+  JOINING_TYPE_D, /* 0636; DAD; D; SAD */
+  JOINING_TYPE_D, /* 0637; TAH; D; TAH */
+  JOINING_TYPE_D, /* 0638; ZAH; D; TAH */
+  JOINING_TYPE_D, /* 0639; AIN; D; AIN */
+  JOINING_TYPE_D, /* 063A; GHAIN; D; AIN */
+  JOINING_TYPE_D, /* 063B; KEHEH WITH 2 DOTS ABOVE; D; GAF */
+  JOINING_TYPE_D, /* 063C; KEHEH WITH 3 DOTS BELOW; D; GAF */
+  JOINING_TYPE_D, /* 063D; FARSI YEH WITH INVERTED V; D; FARSI YEH */
+  JOINING_TYPE_D, /* 063E; FARSI YEH WITH 2 DOTS ABOVE; D; FARSI YEH */
+  JOINING_TYPE_D, /* 063F; FARSI YEH WITH 3 DOTS ABOVE; D; FARSI YEH */
+  JOINING_TYPE_C, /* 0640; TATWEEL; C; No_Joining_Group */
+  JOINING_TYPE_D, /* 0641; FEH; D; FEH */
+  JOINING_TYPE_D, /* 0642; QAF; D; QAF */
+  JOINING_TYPE_D, /* 0643; KAF; D; KAF */
+  JOINING_TYPE_D, /* 0644; LAM; D; LAM */
+  JOINING_TYPE_D, /* 0645; MEEM; D; MEEM */
+  JOINING_TYPE_D, /* 0646; NOON; D; NOON */
+  JOINING_TYPE_D, /* 0647; HEH; D; HEH */
+  JOINING_TYPE_R, /* 0648; WAW; R; WAW */
+  JOINING_TYPE_D, /* 0649; ALEF MAKSURA; D; YEH */
+  JOINING_TYPE_D, /* 064A; YEH; D; YEH */
+  JOINING_TYPE_X, /* 064B */
+  JOINING_TYPE_X, /* 064C */
+  JOINING_TYPE_X, /* 064D */
+  JOINING_TYPE_X, /* 064E */
+  JOINING_TYPE_X, /* 064F */
+  JOINING_TYPE_X, /* 0650 */
+  JOINING_TYPE_X, /* 0651 */
+  JOINING_TYPE_X, /* 0652 */
+  JOINING_TYPE_X, /* 0653 */
+  JOINING_TYPE_X, /* 0654 */
+  JOINING_TYPE_X, /* 0655 */
+  JOINING_TYPE_X, /* 0656 */
+  JOINING_TYPE_X, /* 0657 */
+  JOINING_TYPE_X, /* 0658 */
+  JOINING_TYPE_X, /* 0659 */
+  JOINING_TYPE_X, /* 065A */
+  JOINING_TYPE_X, /* 065B */
+  JOINING_TYPE_X, /* 065C */
+  JOINING_TYPE_X, /* 065D */
+  JOINING_TYPE_X, /* 065E */
+  JOINING_TYPE_X, /* 065F */
+  JOINING_TYPE_X, /* 0660 */
+  JOINING_TYPE_X, /* 0661 */
+  JOINING_TYPE_X, /* 0662 */
+  JOINING_TYPE_X, /* 0663 */
+  JOINING_TYPE_X, /* 0664 */
+  JOINING_TYPE_X, /* 0665 */
+  JOINING_TYPE_X, /* 0666 */
+  JOINING_TYPE_X, /* 0667 */
+  JOINING_TYPE_X, /* 0668 */
+  JOINING_TYPE_X, /* 0669 */
+  JOINING_TYPE_X, /* 066A */
+  JOINING_TYPE_X, /* 066B */
+  JOINING_TYPE_X, /* 066C */
+  JOINING_TYPE_X, /* 066D */
+  JOINING_TYPE_D, /* 066E; DOTLESS BEH; D; BEH */
+  JOINING_TYPE_D, /* 066F; DOTLESS QAF; D; QAF */
+  JOINING_TYPE_X, /* 0670 */
+  JOINING_TYPE_R, /* 0671; HAMZAT WASL ON ALEF; R; ALEF */
+  JOINING_TYPE_R, /* 0672; WAVY HAMZA ON ALEF; R; ALEF */
+  JOINING_TYPE_R, /* 0673; WAVY HAMZA UNDER ALEF; R; ALEF */
+  JOINING_TYPE_U, /* 0674; HIGH HAMZA; U; No_Joining_Group */
+  JOINING_TYPE_R, /* 0675; HIGH HAMZA ALEF; R; ALEF */
+  JOINING_TYPE_R, /* 0676; HIGH HAMZA WAW; R; WAW */
+  JOINING_TYPE_R, /* 0677; HIGH HAMZA WAW WITH DAMMA; R; WAW */
+  JOINING_TYPE_D, /* 0678; HIGH HAMZA YEH; D; YEH */
+  JOINING_TYPE_D, /* 0679; TEH WITH SMALL TAH; D; BEH */
+  JOINING_TYPE_D, /* 067A; TEH WITH 2 DOTS VERTICAL ABOVE; D; BEH */
+  JOINING_TYPE_D, /* 067B; BEH WITH 2 DOTS VERTICAL BELOW; D; BEH */
+  JOINING_TYPE_D, /* 067C; TEH WITH RING; D; BEH */
+  JOINING_TYPE_D, /* 067D; TEH WITH 3 DOTS ABOVE DOWNWARD; D; BEH */
+  JOINING_TYPE_D, /* 067E; TEH WITH 3 DOTS BELOW; D; BEH */
+  JOINING_TYPE_D, /* 067F; TEH WITH 4 DOTS ABOVE; D; BEH */
+  JOINING_TYPE_D, /* 0680; BEH WITH 4 DOTS BELOW; D; BEH */
+  JOINING_TYPE_D, /* 0681; HAMZA ON HAH; D; HAH */
+  JOINING_TYPE_D, /* 0682; HAH WITH 2 DOTS VERTICAL ABOVE; D; HAH */
+  JOINING_TYPE_D, /* 0683; HAH WITH MIDDLE 2 DOTS; D; HAH */
+  JOINING_TYPE_D, /* 0684; HAH WITH MIDDLE 2 DOTS VERTICAL; D; HAH */
+  JOINING_TYPE_D, /* 0685; HAH WITH 3 DOTS ABOVE; D; HAH */
+  JOINING_TYPE_D, /* 0686; HAH WITH MIDDLE 3 DOTS DOWNWARD; D; HAH */
+  JOINING_TYPE_D, /* 0687; HAH WITH MIDDLE 4 DOTS; D; HAH */
+  JOINING_TYPE_R, /* 0688; DAL WITH SMALL TAH; R; DAL */
+  JOINING_TYPE_R, /* 0689; DAL WITH RING; R; DAL */
+  JOINING_TYPE_R, /* 068A; DAL WITH DOT BELOW; R; DAL */
+  JOINING_TYPE_R, /* 068B; DAL WITH DOT BELOW AND SMALL TAH; R; DAL */
+  JOINING_TYPE_R, /* 068C; DAL WITH 2 DOTS ABOVE; R; DAL */
+  JOINING_TYPE_R, /* 068D; DAL WITH 2 DOTS BELOW; R; DAL */
+  JOINING_TYPE_R, /* 068E; DAL WITH 3 DOTS ABOVE; R; DAL */
+  JOINING_TYPE_R, /* 068F; DAL WITH 3 DOTS ABOVE DOWNWARD; R; DAL */
+  JOINING_TYPE_R, /* 0690; DAL WITH 4 DOTS ABOVE; R; DAL */
+  JOINING_TYPE_R, /* 0691; REH WITH SMALL TAH; R; REH */
+  JOINING_TYPE_R, /* 0692; REH WITH SMALL V; R; REH */
+  JOINING_TYPE_R, /* 0693; REH WITH RING; R; REH */
+  JOINING_TYPE_R, /* 0694; REH WITH DOT BELOW; R; REH */
+  JOINING_TYPE_R, /* 0695; REH WITH SMALL V BELOW; R; REH */
+  JOINING_TYPE_R, /* 0696; REH WITH DOT BELOW AND DOT ABOVE; R; REH */
+  JOINING_TYPE_R, /* 0697; REH WITH 2 DOTS ABOVE; R; REH */
+  JOINING_TYPE_R, /* 0698; REH WITH 3 DOTS ABOVE; R; REH */
+  JOINING_TYPE_R, /* 0699; REH WITH 4 DOTS ABOVE; R; REH */
+  JOINING_TYPE_D, /* 069A; SEEN WITH DOT BELOW AND DOT ABOVE; D; SEEN */
+  JOINING_TYPE_D, /* 069B; SEEN WITH 3 DOTS BELOW; D; SEEN */
+  JOINING_TYPE_D, /* 069C; SEEN WITH 3 DOTS BELOW AND 3 DOTS ABOVE; D; SEEN */
+  JOINING_TYPE_D, /* 069D; SAD WITH 2 DOTS BELOW; D; SAD */
+  JOINING_TYPE_D, /* 069E; SAD WITH 3 DOTS ABOVE; D; SAD */
+  JOINING_TYPE_D, /* 069F; TAH WITH 3 DOTS ABOVE; D; TAH */
+  JOINING_TYPE_D, /* 06A0; AIN WITH 3 DOTS ABOVE; D; AIN */
+  JOINING_TYPE_D, /* 06A1; DOTLESS FEH; D; FEH */
+  JOINING_TYPE_D, /* 06A2; FEH WITH DOT MOVED BELOW; D; FEH */
+  JOINING_TYPE_D, /* 06A3; FEH WITH DOT BELOW; D; FEH */
+  JOINING_TYPE_D, /* 06A4; FEH WITH 3 DOTS ABOVE; D; FEH */
+  JOINING_TYPE_D, /* 06A5; FEH WITH 3 DOTS BELOW; D; FEH */
+  JOINING_TYPE_D, /* 06A6; FEH WITH 4 DOTS ABOVE; D; FEH */
+  JOINING_TYPE_D, /* 06A7; QAF WITH DOT ABOVE; D; QAF */
+  JOINING_TYPE_D, /* 06A8; QAF WITH 3 DOTS ABOVE; D; QAF */
+  JOINING_TYPE_D, /* 06A9; KEHEH; D; GAF */
+  JOINING_TYPE_D, /* 06AA; SWASH KAF; D; SWASH KAF */
+  JOINING_TYPE_D, /* 06AB; KAF WITH RING; D; GAF */
+  JOINING_TYPE_D, /* 06AC; KAF WITH DOT ABOVE; D; KAF */
+  JOINING_TYPE_D, /* 06AD; KAF WITH 3 DOTS ABOVE; D; KAF */
+  JOINING_TYPE_D, /* 06AE; KAF WITH 3 DOTS BELOW; D; KAF */
+  JOINING_TYPE_D, /* 06AF; GAF; D; GAF */
+  JOINING_TYPE_D, /* 06B0; GAF WITH RING; D; GAF */
+  JOINING_TYPE_D, /* 06B1; GAF WITH 2 DOTS ABOVE; D; GAF */
+  JOINING_TYPE_D, /* 06B2; GAF WITH 2 DOTS BELOW; D; GAF */
+  JOINING_TYPE_D, /* 06B3; GAF WITH 2 DOTS VERTICAL BELOW; D; GAF */
+  JOINING_TYPE_D, /* 06B4; GAF WITH 3 DOTS ABOVE; D; GAF */
+  JOINING_TYPE_D, /* 06B5; LAM WITH SMALL V; D; LAM */
+  JOINING_TYPE_D, /* 06B6; LAM WITH DOT ABOVE; D; LAM */
+  JOINING_TYPE_D, /* 06B7; LAM WITH 3 DOTS ABOVE; D; LAM */
+  JOINING_TYPE_D, /* 06B8; LAM WITH 3 DOTS BELOW; D; LAM */
+  JOINING_TYPE_D, /* 06B9; NOON WITH DOT BELOW; D; NOON */
+  JOINING_TYPE_D, /* 06BA; DOTLESS NOON; D; NOON */
+  JOINING_TYPE_D, /* 06BB; DOTLESS NOON WITH SMALL TAH; D; NOON */
+  JOINING_TYPE_D, /* 06BC; NOON WITH RING; D; NOON */
+  JOINING_TYPE_D, /* 06BD; NYA; D; NYA */
+  JOINING_TYPE_D, /* 06BE; KNOTTED HEH; D; KNOTTED HEH */
+  JOINING_TYPE_D, /* 06BF; HAH WITH MIDDLE 3 DOTS DOWNWARD AND DOT ABOVE; D; HAH */
+  JOINING_TYPE_R, /* 06C0; HAMZA ON HEH; R; TEH MARBUTA */
+  JOINING_TYPE_D, /* 06C1; HEH GOAL; D; HEH GOAL */
+  JOINING_TYPE_D, /* 06C2; HAMZA ON HEH GOAL; D; HEH GOAL */
+  JOINING_TYPE_R, /* 06C3; TEH MARBUTA GOAL; R; HAMZA ON HEH GOAL */
+  JOINING_TYPE_R, /* 06C4; WAW WITH RING; R; WAW */
+  JOINING_TYPE_R, /* 06C5; WAW WITH BAR; R; WAW */
+  JOINING_TYPE_R, /* 06C6; WAW WITH SMALL V; R; WAW */
+  JOINING_TYPE_R, /* 06C7; WAW WITH DAMMA; R; WAW */
+  JOINING_TYPE_R, /* 06C8; WAW WITH ALEF ABOVE; R; WAW */
+  JOINING_TYPE_R, /* 06C9; WAW WITH INVERTED SMALL V; R; WAW */
+  JOINING_TYPE_R, /* 06CA; WAW WITH 2 DOTS ABOVE; R; WAW */
+  JOINING_TYPE_R, /* 06CB; WAW WITH 3 DOTS ABOVE; R; WAW */
+  JOINING_TYPE_D, /* 06CC; FARSI YEH; D; FARSI YEH */
+  JOINING_TYPE_R, /* 06CD; YEH WITH TAIL; R; YEH WITH TAIL */
+  JOINING_TYPE_D, /* 06CE; FARSI YEH WITH SMALL V; D; FARSI YEH */
+  JOINING_TYPE_R, /* 06CF; WAW WITH DOT ABOVE; R; WAW */
+  JOINING_TYPE_D, /* 06D0; YEH WITH 2 DOTS VERTICAL BELOW; D; YEH */
+  JOINING_TYPE_D, /* 06D1; YEH WITH 3 DOTS BELOW; D; YEH */
+  JOINING_TYPE_R, /* 06D2; YEH BARREE; R; YEH BARREE */
+  JOINING_TYPE_R, /* 06D3; HAMZA ON YEH BARREE; R; YEH BARREE */
+  JOINING_TYPE_X, /* 06D4 */
+  JOINING_TYPE_R, /* 06D5; AE; R; TEH MARBUTA */
+  JOINING_TYPE_X, /* 06D6 */
+  JOINING_TYPE_X, /* 06D7 */
+  JOINING_TYPE_X, /* 06D8 */
+  JOINING_TYPE_X, /* 06D9 */
+  JOINING_TYPE_X, /* 06DA */
+  JOINING_TYPE_X, /* 06DB */
+  JOINING_TYPE_X, /* 06DC */
+  JOINING_TYPE_U, /* 06DD; ARABIC END OF AYAH; U; No_Joining_Group */
+  JOINING_TYPE_X, /* 06DE */
+  JOINING_TYPE_X, /* 06DF */
+  JOINING_TYPE_X, /* 06E0 */
+  JOINING_TYPE_X, /* 06E1 */
+  JOINING_TYPE_X, /* 06E2 */
+  JOINING_TYPE_X, /* 06E3 */
+  JOINING_TYPE_X, /* 06E4 */
+  JOINING_TYPE_X, /* 06E5 */
+  JOINING_TYPE_X, /* 06E6 */
+  JOINING_TYPE_X, /* 06E7 */
+  JOINING_TYPE_X, /* 06E8 */
+  JOINING_TYPE_X, /* 06E9 */
+  JOINING_TYPE_X, /* 06EA */
+  JOINING_TYPE_X, /* 06EB */
+  JOINING_TYPE_X, /* 06EC */
+  JOINING_TYPE_X, /* 06ED */
+  JOINING_TYPE_R, /* 06EE; DAL WITH INVERTED V; R; DAL */
+  JOINING_TYPE_R, /* 06EF; REH WITH INVERTED V; R; REH */
+  JOINING_TYPE_X, /* 06F0 */
+  JOINING_TYPE_X, /* 06F1 */
+  JOINING_TYPE_X, /* 06F2 */
+  JOINING_TYPE_X, /* 06F3 */
+  JOINING_TYPE_X, /* 06F4 */
+  JOINING_TYPE_X, /* 06F5 */
+  JOINING_TYPE_X, /* 06F6 */
+  JOINING_TYPE_X, /* 06F7 */
+  JOINING_TYPE_X, /* 06F8 */
+  JOINING_TYPE_X, /* 06F9 */
+  JOINING_TYPE_D, /* 06FA; SEEN WITH DOT BELOW AND 3 DOTS ABOVE; D; SEEN */
+  JOINING_TYPE_D, /* 06FB; DAD WITH DOT BELOW; D; SAD */
+  JOINING_TYPE_D, /* 06FC; GHAIN WITH DOT BELOW; D; AIN */
+  JOINING_TYPE_X, /* 06FD */
+  JOINING_TYPE_X, /* 06FE */
+  JOINING_TYPE_D, /* 06FF; HEH WITH INVERTED V; D; KNOTTED HEH */
+  JOINING_TYPE_X, /* 0700 */
+  JOINING_TYPE_X, /* 0701 */
+  JOINING_TYPE_X, /* 0702 */
+  JOINING_TYPE_X, /* 0703 */
+  JOINING_TYPE_X, /* 0704 */
+  JOINING_TYPE_X, /* 0705 */
+  JOINING_TYPE_X, /* 0706 */
+  JOINING_TYPE_X, /* 0707 */
+  JOINING_TYPE_X, /* 0708 */
+  JOINING_TYPE_X, /* 0709 */
+  JOINING_TYPE_X, /* 070A */
+  JOINING_TYPE_X, /* 070B */
+  JOINING_TYPE_X, /* 070C */
+  JOINING_TYPE_X, /* 070D */
+  JOINING_TYPE_X, /* 070E */
+  JOINING_TYPE_X, /* 070F */
+  JOINING_GROUP_ALAPH, /* 0710; ALAPH; R; ALAPH */
+  JOINING_TYPE_X, /* 0711 */
+  JOINING_TYPE_D, /* 0712; BETH; D; BETH */
+  JOINING_TYPE_D, /* 0713; GAMAL; D; GAMAL */
+  JOINING_TYPE_D, /* 0714; GAMAL GARSHUNI; D; GAMAL */
+  JOINING_GROUP_DALATH_RISH, /* 0715; DALATH; R; DALATH RISH */
+  JOINING_GROUP_DALATH_RISH, /* 0716; DOTLESS DALATH RISH; R; DALATH RISH */
+  JOINING_TYPE_R, /* 0717; HE; R; HE */
+  JOINING_TYPE_R, /* 0718; WAW; R; SYRIAC WAW */
+  JOINING_TYPE_R, /* 0719; ZAIN; R; ZAIN */
+  JOINING_TYPE_D, /* 071A; HETH; D; HETH */
+  JOINING_TYPE_D, /* 071B; TETH; D; TETH */
+  JOINING_TYPE_D, /* 071C; TETH GARSHUNI; D; TETH */
+  JOINING_TYPE_D, /* 071D; YUDH; D; YUDH */
+  JOINING_TYPE_R, /* 071E; YUDH HE; R; YUDH HE */
+  JOINING_TYPE_D, /* 071F; KAPH; D; KAPH */
+  JOINING_TYPE_D, /* 0720; LAMADH; D; LAMADH */
+  JOINING_TYPE_D, /* 0721; MIM; D; MIM */
+  JOINING_TYPE_D, /* 0722; NUN; D; NUN */
+  JOINING_TYPE_D, /* 0723; SEMKATH; D; SEMKATH */
+  JOINING_TYPE_D, /* 0724; FINAL SEMKATH; D; FINAL SEMKATH */
+  JOINING_TYPE_D, /* 0725; E; D; E */
+  JOINING_TYPE_D, /* 0726; PE; D; PE */
+  JOINING_TYPE_D, /* 0727; REVERSED PE; D; REVERSED PE */
+  JOINING_TYPE_R, /* 0728; SADHE; R; SADHE */
+  JOINING_TYPE_D, /* 0729; QAPH; D; QAPH */
+  JOINING_GROUP_DALATH_RISH, /* 072A; RISH; R; DALATH RISH */
+  JOINING_TYPE_D, /* 072B; SHIN; D; SHIN */
+  JOINING_TYPE_R, /* 072C; TAW; R; TAW */
+  JOINING_TYPE_D, /* 072D; PERSIAN BHETH; D; BETH */
+  JOINING_TYPE_D, /* 072E; PERSIAN GHAMAL; D; GAMAL */
+  JOINING_GROUP_DALATH_RISH, /* 072F; PERSIAN DHALATH; R; DALATH RISH */
+  JOINING_TYPE_X, /* 0730 */
+  JOINING_TYPE_X, /* 0731 */
+  JOINING_TYPE_X, /* 0732 */
+  JOINING_TYPE_X, /* 0733 */
+  JOINING_TYPE_X, /* 0734 */
+  JOINING_TYPE_X, /* 0735 */
+  JOINING_TYPE_X, /* 0736 */
+  JOINING_TYPE_X, /* 0737 */
+  JOINING_TYPE_X, /* 0738 */
+  JOINING_TYPE_X, /* 0739 */
+  JOINING_TYPE_X, /* 073A */
+  JOINING_TYPE_X, /* 073B */
+  JOINING_TYPE_X, /* 073C */
+  JOINING_TYPE_X, /* 073D */
+  JOINING_TYPE_X, /* 073E */
+  JOINING_TYPE_X, /* 073F */
+  JOINING_TYPE_X, /* 0740 */
+  JOINING_TYPE_X, /* 0741 */
+  JOINING_TYPE_X, /* 0742 */
+  JOINING_TYPE_X, /* 0743 */
+  JOINING_TYPE_X, /* 0744 */
+  JOINING_TYPE_X, /* 0745 */
+  JOINING_TYPE_X, /* 0746 */
+  JOINING_TYPE_X, /* 0747 */
+  JOINING_TYPE_X, /* 0748 */
+  JOINING_TYPE_X, /* 0749 */
+  JOINING_TYPE_X, /* 074A */
+  JOINING_TYPE_X, /* 074B */
+  JOINING_TYPE_X, /* 074C */
+  JOINING_TYPE_R, /* 074D; SOGDIAN ZHAIN; R; ZHAIN */
+  JOINING_TYPE_D, /* 074E; SOGDIAN KHAPH; D; KHAPH */
+  JOINING_TYPE_D, /* 074F; SOGDIAN FE; D; FE */
+  JOINING_TYPE_D, /* 0750; BEH WITH 3 DOTS HORIZONTALLY BELOW; D; BEH */
+  JOINING_TYPE_D, /* 0751; BEH WITH DOT BELOW AND 3 DOTS ABOVE; D; BEH */
+  JOINING_TYPE_D, /* 0752; BEH WITH 3 DOTS POINTING UPWARDS BELOW; D; BEH */
+  JOINING_TYPE_D, /* 0753; BEH WITH 3 DOTS POINTING UPWARDS BELOW AND 2 DOTS ABOVE; D; BEH */
+  JOINING_TYPE_D, /* 0754; BEH WITH 2 DOTS BELOW AND DOT ABOVE; D; BEH */
+  JOINING_TYPE_D, /* 0755; BEH WITH INVERTED SMALL V BELOW; D; BEH */
+  JOINING_TYPE_D, /* 0756; BEH WITH SMALL V; D; BEH */
+  JOINING_TYPE_D, /* 0757; HAH WITH 2 DOTS ABOVE; D; HAH */
+  JOINING_TYPE_D, /* 0758; HAH WITH 3 DOTS POINTING UPWARDS BELOW; D; HAH */
+  JOINING_TYPE_R, /* 0759; DAL WITH 2 DOTS VERTICALLY BELOW AND SMALL TAH; R; DAL */
+  JOINING_TYPE_R, /* 075A; DAL WITH INVERTED SMALL V BELOW; R; DAL */
+  JOINING_TYPE_R, /* 075B; REH WITH STROKE; R; REH */
+  JOINING_TYPE_D, /* 075C; SEEN WITH 4 DOTS ABOVE; D; SEEN */
+  JOINING_TYPE_D, /* 075D; AIN WITH 2 DOTS ABOVE; D; AIN */
+  JOINING_TYPE_D, /* 075E; AIN WITH 3 DOTS POINTING DOWNWARDS ABOVE; D; AIN */
+  JOINING_TYPE_D, /* 075F; AIN WITH 2 DOTS VERTICALLY ABOVE; D; AIN */
+  JOINING_TYPE_D, /* 0760; FEH WITH 2 DOTS BELOW; D; FEH */
+  JOINING_TYPE_D, /* 0761; FEH WITH 3 DOTS POINTING UPWARDS BELOW; D; FEH */
+  JOINING_TYPE_D, /* 0762; KEHEH WITH DOT ABOVE; D; GAF */
+  JOINING_TYPE_D, /* 0763; KEHEH WITH 3 DOTS ABOVE; D; GAF */
+  JOINING_TYPE_D, /* 0764; KEHEH WITH 3 DOTS POINTING UPWARDS BELOW; D; GAF */
+  JOINING_TYPE_D, /* 0765; MEEM WITH DOT ABOVE; D; MEEM */
+  JOINING_TYPE_D, /* 0766; MEEM WITH DOT BELOW; D; MEEM */
+  JOINING_TYPE_D, /* 0767; NOON WITH 2 DOTS BELOW; D; NOON */
+  JOINING_TYPE_D, /* 0768; NOON WITH SMALL TAH; D; NOON */
+  JOINING_TYPE_D, /* 0769; NOON WITH SMALL V; D; NOON */
+  JOINING_TYPE_D, /* 076A; LAM WITH BAR; D; LAM */
+  JOINING_TYPE_R, /* 076B; REH WITH 2 DOTS VERTICALLY ABOVE; R; REH */
+  JOINING_TYPE_R, /* 076C; REH WITH HAMZA ABOVE; R; REH */
+  JOINING_TYPE_D, /* 076D; SEEN WITH 2 DOTS VERTICALLY ABOVE; D; SEEN */
+  JOINING_TYPE_D, /* 076E; HAH WITH SMALL TAH BELOW; D; HAH */
+  JOINING_TYPE_D, /* 076F; HAH WITH SMALL TAH AND 2 DOTS; D; HAH */
+  JOINING_TYPE_D, /* 0770; SEEN WITH SMALL TAH AND 2 DOTS; D; SEEN */
+  JOINING_TYPE_R, /* 0771; REH WITH SMALL TAH AND 2 DOTS; R; REH */
+  JOINING_TYPE_D, /* 0772; HAH WITH SMALL TAH ABOVE; D; HAH */
+  JOINING_TYPE_R, /* 0773; ALEF WITH DIGIT TWO ABOVE; R; ALEF */
+  JOINING_TYPE_R, /* 0774; ALEF WITH DIGIT THREE ABOVE; R; ALEF */
+  JOINING_TYPE_D, /* 0775; FARSI YEH WITH DIGIT TWO ABOVE; D; FARSI YEH */
+  JOINING_TYPE_D, /* 0776; FARSI YEH WITH DIGIT THREE ABOVE; D; FARSI YEH */
+  JOINING_TYPE_D, /* 0777; YEH WITH DIGIT FOUR BELOW; D; YEH */
+  JOINING_TYPE_R, /* 0778; WAW WITH DIGIT TWO ABOVE; R; WAW */
+  JOINING_TYPE_R, /* 0779; WAW WITH DIGIT THREE ABOVE; R; WAW */
+  JOINING_TYPE_D, /* 077A; YEH BARREE WITH DIGIT TWO ABOVE; D; BURUSHASKI YEH BARREE */
+  JOINING_TYPE_D, /* 077B; YEH BARREE WITH DIGIT THREE ABOVE; D; BURUSHASKI YEH BARREE */
+  JOINING_TYPE_D, /* 077C; HAH WITH DIGIT FOUR BELOW; D; HAH */
+  JOINING_TYPE_D, /* 077D; SEEN WITH DIGIT FOUR ABOVE; D; SEEN */
+  JOINING_TYPE_D, /* 077E; SEEN WITH INVERTED V; D; SEEN */
+  JOINING_TYPE_D, /* 077F; KAF WITH 2 DOTS ABOVE; D; KAF */
+  JOINING_TYPE_X, /* 0780 */
+  JOINING_TYPE_X, /* 0781 */
+  JOINING_TYPE_X, /* 0782 */
+  JOINING_TYPE_X, /* 0783 */
+  JOINING_TYPE_X, /* 0784 */
+  JOINING_TYPE_X, /* 0785 */
+  JOINING_TYPE_X, /* 0786 */
+  JOINING_TYPE_X, /* 0787 */
+  JOINING_TYPE_X, /* 0788 */
+  JOINING_TYPE_X, /* 0789 */
+  JOINING_TYPE_X, /* 078A */
+  JOINING_TYPE_X, /* 078B */
+  JOINING_TYPE_X, /* 078C */
+  JOINING_TYPE_X, /* 078D */
+  JOINING_TYPE_X, /* 078E */
+  JOINING_TYPE_X, /* 078F */
+  JOINING_TYPE_X, /* 0790 */
+  JOINING_TYPE_X, /* 0791 */
+  JOINING_TYPE_X, /* 0792 */
+  JOINING_TYPE_X, /* 0793 */
+  JOINING_TYPE_X, /* 0794 */
+  JOINING_TYPE_X, /* 0795 */
+  JOINING_TYPE_X, /* 0796 */
+  JOINING_TYPE_X, /* 0797 */
+  JOINING_TYPE_X, /* 0798 */
+  JOINING_TYPE_X, /* 0799 */
+  JOINING_TYPE_X, /* 079A */
+  JOINING_TYPE_X, /* 079B */
+  JOINING_TYPE_X, /* 079C */
+  JOINING_TYPE_X, /* 079D */
+  JOINING_TYPE_X, /* 079E */
+  JOINING_TYPE_X, /* 079F */
+  JOINING_TYPE_X, /* 07A0 */
+  JOINING_TYPE_X, /* 07A1 */
+  JOINING_TYPE_X, /* 07A2 */
+  JOINING_TYPE_X, /* 07A3 */
+  JOINING_TYPE_X, /* 07A4 */
+  JOINING_TYPE_X, /* 07A5 */
+  JOINING_TYPE_X, /* 07A6 */
+  JOINING_TYPE_X, /* 07A7 */
+  JOINING_TYPE_X, /* 07A8 */
+  JOINING_TYPE_X, /* 07A9 */
+  JOINING_TYPE_X, /* 07AA */
+  JOINING_TYPE_X, /* 07AB */
+  JOINING_TYPE_X, /* 07AC */
+  JOINING_TYPE_X, /* 07AD */
+  JOINING_TYPE_X, /* 07AE */
+  JOINING_TYPE_X, /* 07AF */
+  JOINING_TYPE_X, /* 07B0 */
+  JOINING_TYPE_X, /* 07B1 */
+  JOINING_TYPE_X, /* 07B2 */
+  JOINING_TYPE_X, /* 07B3 */
+  JOINING_TYPE_X, /* 07B4 */
+  JOINING_TYPE_X, /* 07B5 */
+  JOINING_TYPE_X, /* 07B6 */
+  JOINING_TYPE_X, /* 07B7 */
+  JOINING_TYPE_X, /* 07B8 */
+  JOINING_TYPE_X, /* 07B9 */
+  JOINING_TYPE_X, /* 07BA */
+  JOINING_TYPE_X, /* 07BB */
+  JOINING_TYPE_X, /* 07BC */
+  JOINING_TYPE_X, /* 07BD */
+  JOINING_TYPE_X, /* 07BE */
+  JOINING_TYPE_X, /* 07BF */
+  JOINING_TYPE_X, /* 07C0 */
+  JOINING_TYPE_X, /* 07C1 */
+  JOINING_TYPE_X, /* 07C2 */
+  JOINING_TYPE_X, /* 07C3 */
+  JOINING_TYPE_X, /* 07C4 */
+  JOINING_TYPE_X, /* 07C5 */
+  JOINING_TYPE_X, /* 07C6 */
+  JOINING_TYPE_X, /* 07C7 */
+  JOINING_TYPE_X, /* 07C8 */
+  JOINING_TYPE_X, /* 07C9 */
+  JOINING_TYPE_D, /* 07CA; NKO A; D; No_Joining_Group */
+  JOINING_TYPE_D, /* 07CB; NKO EE; D; No_Joining_Group */
+  JOINING_TYPE_D, /* 07CC; NKO I; D; No_Joining_Group */
+  JOINING_TYPE_D, /* 07CD; NKO E; D; No_Joining_Group */
+  JOINING_TYPE_D, /* 07CE; NKO U; D; No_Joining_Group */
+  JOINING_TYPE_D, /* 07CF; NKO OO; D; No_Joining_Group */
+  JOINING_TYPE_D, /* 07D0; NKO O; D; No_Joining_Group */
+  JOINING_TYPE_D, /* 07D1; NKO DAGBASINNA; D; No_Joining_Group */
+  JOINING_TYPE_D, /* 07D2; NKO N; D; No_Joining_Group */
+  JOINING_TYPE_D, /* 07D3; NKO BA; D; No_Joining_Group */
+  JOINING_TYPE_D, /* 07D4; NKO PA; D; No_Joining_Group */
+  JOINING_TYPE_D, /* 07D5; NKO TA; D; No_Joining_Group */
+  JOINING_TYPE_D, /* 07D6; NKO JA; D; No_Joining_Group */
+  JOINING_TYPE_D, /* 07D7; NKO CHA; D; No_Joining_Group */
+  JOINING_TYPE_D, /* 07D8; NKO DA; D; No_Joining_Group */
+  JOINING_TYPE_D, /* 07D9; NKO RA; D; No_Joining_Group */
+  JOINING_TYPE_D, /* 07DA; NKO RRA; D; No_Joining_Group */
+  JOINING_TYPE_D, /* 07DB; NKO SA; D; No_Joining_Group */
+  JOINING_TYPE_D, /* 07DC; NKO GBA; D; No_Joining_Group */
+  JOINING_TYPE_D, /* 07DD; NKO FA; D; No_Joining_Group */
+  JOINING_TYPE_D, /* 07DE; NKO KA; D; No_Joining_Group */
+  JOINING_TYPE_D, /* 07DF; NKO LA; D; No_Joining_Group */
+  JOINING_TYPE_D, /* 07E0; NKO NA WOLOSO; D; No_Joining_Group */
+  JOINING_TYPE_D, /* 07E1; NKO MA; D; No_Joining_Group */
+  JOINING_TYPE_D, /* 07E2; NKO NYA; D; No_Joining_Group */
+  JOINING_TYPE_D, /* 07E3; NKO NA; D; No_Joining_Group */
+  JOINING_TYPE_D, /* 07E4; NKO HA; D; No_Joining_Group */
+  JOINING_TYPE_D, /* 07E5; NKO WA; D; No_Joining_Group */
+  JOINING_TYPE_D, /* 07E6; NKO YA; D; No_Joining_Group */
+  JOINING_TYPE_D, /* 07E7; NKO NYA WOLOSO; D; No_Joining_Group */
+  JOINING_TYPE_D, /* 07E8; NKO JONA JA; D; No_Joining_Group */
+  JOINING_TYPE_D, /* 07E9; NKO JONA CHA; D; No_Joining_Group */
+  JOINING_TYPE_D, /* 07EA; NKO JONA RA; D; No_Joining_Group */
+  JOINING_TYPE_X, /* 07EB */
+  JOINING_TYPE_X, /* 07EC */
+  JOINING_TYPE_X, /* 07ED */
+  JOINING_TYPE_X, /* 07EE */
+  JOINING_TYPE_X, /* 07EF */
+  JOINING_TYPE_X, /* 07F0 */
+  JOINING_TYPE_X, /* 07F1 */
+  JOINING_TYPE_X, /* 07F2 */
+  JOINING_TYPE_X, /* 07F3 */
+  JOINING_TYPE_X, /* 07F4 */
+  JOINING_TYPE_X, /* 07F5 */
+  JOINING_TYPE_X, /* 07F6 */
+  JOINING_TYPE_X, /* 07F7 */
+  JOINING_TYPE_X, /* 07F8 */
+  JOINING_TYPE_X, /* 07F9 */
+  JOINING_TYPE_C, /* 07FA; NKO LAJANYALAN; C; No_Joining_Group */
+  JOINING_TYPE_X, /* 07FB */
+  JOINING_TYPE_X, /* 07FC */
+  JOINING_TYPE_X, /* 07FD */
+  JOINING_TYPE_X, /* 07FE */
+  JOINING_TYPE_X, /* 07FF */
+  /* == End of generated table == */
+  JOINING_TYPE_X
+};
+
+static unsigned int get_joining_type (hb_codepoint_t u, hb_category_t gen_cat)
+{
+  /* TODO Macroize the magic bit operations */
+
+  if (likely ((u & ~(0x0600^0x07FF)) == 0x0600)) {
+    unsigned int j_type = arabic_syriac_nko_joining_types[u - 0x0600];
+    if (likely (j_type != JOINING_TYPE_X))
+      return j_type;
+  }
+
+  if (unlikely ((u & ~(0x200C^0x200D)) == 0x200C)) {
+    return u == 0x200C ? JOINING_TYPE_U : JOINING_TYPE_C;
+  }
+
+  return ((1<<gen_cat) & ((1<<HB_CATEGORY_NON_SPACING_MARK)|(1<<HB_CATEGORY_ENCLOSING_MARK)|(1<<HB_CATEGORY_FORMAT))) ?
+	 JOINING_TYPE_T : JOINING_TYPE_U;
+}
+
+
+
+static const hb_tag_t arabic_syriac_features[] =
+{
+  HB_TAG('i','n','i','t'),
+  HB_TAG('m','e','d','i'),
+  HB_TAG('f','i','n','a'),
+  HB_TAG('i','s','o','l'),
+  /* Syriac */
+  HB_TAG('m','e','d','2'),
+  HB_TAG('f','i','n','2'),
+  HB_TAG('f','i','n','3'),
+  HB_TAG_NONE
+};
+
+
+/* Same order as the feature array */
+enum {
+  INIT,
+  MEDI,
+  FINA,
+  ISOL,
+
+  /* Syriac */
+  MED2,
+  FIN2,
+  FIN3,
+
+  NONE,
+
+  COMMON_NUM_FEATURES = 4,
+  SYRIAC_NUM_FEATURES = 7,
+  TOTAL_NUM_FEATURES = NONE
+};
+
+static const struct arabic_state_table_entry {
+	uint8_t prev_action;
+	uint8_t curr_action;
+	uint8_t next_state;
+	uint8_t padding;
+} arabic_state_table[][NUM_STATE_MACHINE_COLS] =
+{
+  /*   jt_U,          jt_R,          jt_D,          jg_ALAPH,      jg_DALATH_RISH */
+
+  /* State 0: prev was U, not willing to join. */
+  { {NONE,NONE,0}, {NONE,ISOL,1}, {NONE,ISOL,2}, {NONE,ISOL,1}, {NONE,ISOL,6}, },
+
+  /* State 1: prev was R or ISOL/ALAPH, not willing to join. */
+  { {NONE,NONE,0}, {NONE,ISOL,1}, {NONE,ISOL,2}, {NONE,FIN2,5}, {NONE,ISOL,6}, },
+
+  /* State 2: prev was D/ISOL, willing to join. */
+  { {NONE,NONE,0}, {INIT,FINA,1}, {INIT,FINA,3}, {INIT,FINA,4}, {INIT,FINA,6}, },
+
+  /* State 3: prev was D/FINA, willing to join. */
+  { {NONE,NONE,0}, {MEDI,FINA,1}, {MEDI,FINA,3}, {MEDI,FINA,4}, {MEDI,FINA,6}, },
+
+  /* State 4: prev was FINA ALAPH, not willing to join. */
+  { {NONE,NONE,0}, {MED2,ISOL,1}, {MED2,ISOL,2}, {MED2,FIN2,5}, {MED2,ISOL,6}, },
+
+  /* State 5: prev was FIN2/FIN3 ALAPH, not willing to join. */
+  { {NONE,NONE,0}, {ISOL,ISOL,1}, {ISOL,ISOL,2}, {ISOL,FIN2,5}, {ISOL,ISOL,6}, },
+
+  /* State 6: prev was DALATH/RISH, not willing to join. */
+  { {NONE,NONE,0}, {NONE,ISOL,1}, {NONE,ISOL,2}, {NONE,FIN3,5}, {NONE,ISOL,6}, }
+};
+
+
+void
+_hb_ot_analyze_complex_arabic (hb_font_t    *font,
+			       hb_face_t    *face,
+			       hb_buffer_t  *buffer,
+			       hb_feature_t *features HB_UNUSED,
+			       unsigned int  num_features HB_UNUSED)
+{
+  unsigned int count = buffer->len;
+  unsigned int prev = 0, state = 0;
+
+  for (unsigned int i = 0; i < count; i++) {
+
+    unsigned int this_type = get_joining_type (buffer->info[i].codepoint, buffer->unicode->v.get_general_category (buffer->info[i].codepoint));
+
+    if (unlikely (this_type == JOINING_TYPE_T))
+      continue;
+
+    const arabic_state_table_entry *entry = arabic_state_table[state];
+
+    if (entry->prev_action != NONE)
+      buffer->info[prev].gproperty = entry->prev_action;
+
+    buffer->info[i].gproperty = entry->curr_action;
+
+    prev = i;
+    state = entry->next_state;
+  }
+
+  hb_mask_t mask_array[TOTAL_NUM_FEATURES] = {0};
+  unsigned int num_masks = buffer->props.script == HB_SCRIPT_SYRIAC ? SYRIAC_NUM_FEATURES : COMMON_NUM_FEATURES;
+  for (unsigned int i = 0; i < num_masks; i++)
+    mask_array[i] = 0 /* XXX find_mask */;
+
+  for (unsigned int i = 0; i < count; i++)
+    buffer->info[i].mask |= mask_array[buffer->info[i].gproperty];
+}
+
+
+HB_END_DECLS
commit 0109816b50064a314389333ff64aaf22cb4b1e56
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Tue Oct 5 18:36:45 2010 -0400

    Update build system

diff --git a/autogen.sh b/autogen.sh
index e4317db..fb0148b 100755
--- a/autogen.sh
+++ b/autogen.sh
@@ -171,9 +171,6 @@ do_cmd $AUTOHEADER
 
 touch ChangeLog
 
-# We don't call gtkdocize right now.  When we do, we should then modify
-# the generated gtk-doc.make and move it to build/Makefile.am.gtk-doc.
-# See that file for details.
 #do_cmd $GTKDOCIZE $GTKDOCIZE_FLAGS
 
 do_cmd $AUTOMAKE $AUTOMAKE_FLAGS
diff --git a/configure.ac b/configure.ac
index 721c912..56c6323 100644
--- a/configure.ac
+++ b/configure.ac
@@ -9,6 +9,7 @@ AC_LIBTOOL_WIN32_DLL
 AC_PROG_LIBTOOL dnl ([1.4]) Don't remove!
 
 AC_PROG_CC
+AM_PROG_CC_C_O
 AC_PROG_CXX
 
 AC_CHECK_FUNCS(mprotect sysconf getpagesize)
commit e81d7afe6e0e9dd26025f3243a11cf0b408a8046
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Mon Oct 4 18:18:48 2010 -0400

    Add hb_face_get_upem()

diff --git a/src/hb-font.cc b/src/hb-font.cc
index fd5df93..223b2b4 100644
--- a/src/hb-font.cc
+++ b/src/hb-font.cc
@@ -397,6 +397,12 @@ hb_face_get_table (hb_face_t *face,
   return blob;
 }
 
+unsigned int
+hb_face_get_upem (hb_face_t *face)
+{
+  return (face->head_table ? face->head_table : &Null(head))->get_upem ();
+}
+
 
 /*
  * hb_font_t
diff --git a/src/hb-font.h b/src/hb-font.h
index 7815985..934f17c 100644
--- a/src/hb-font.h
+++ b/src/hb-font.h
@@ -78,6 +78,9 @@ hb_blob_t *
 hb_face_get_table (hb_face_t *face,
 		   hb_tag_t   tag);
 
+unsigned int
+hb_face_get_upem (hb_face_t *face);
+
 
 /*
  * hb_font_funcs_t
commit d47f79db92fa45d51cd5f7845db8a206f5ec122b
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Mon Oct 4 18:13:30 2010 -0400

    Add TODO item

diff --git a/TODO b/TODO
index 2537bb6..001f437 100644
--- a/TODO
+++ b/TODO
@@ -1,5 +1,7 @@
 - Rename get_table to reference_table
 
+- Avoid allocating blob objects internally for for_data() faces
+
 - head table access cleanup (div by zero now!)
 - cache various expensive scale computation
 
commit bd361945a89ea31e6c4525aa030e18744ea81fb3
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Mon Oct 4 17:22:57 2010 -0400

    Add API comments

diff --git a/src/hb-font.h b/src/hb-font.h
index 266b74a..7815985 100644
--- a/src/hb-font.h
+++ b/src/hb-font.h
@@ -61,7 +61,19 @@ hb_face_get_reference_count (hb_face_t *face);
 void
 hb_face_destroy (hb_face_t *face);
 
-/* Returns NULL if not found */
+/* XXX
+ *
+ * I have two major concerns about this API as it is right now:
+ *
+ *   - Jonathan Kew convinced me to make it return NULL if table not found (280af1bd),
+ *     however, that is WRONG IMO.  The API should not differentiate between a non-existing
+ *     table vs a zero-length table vs a very short table.  It only leads to implementations
+ *     that check for non-NULL and assume that they've got a usable table going on...  This
+ *     actually happened with Firefox.
+ *
+ *   - It has to be renamed to reference_table() since unlike any other _get_ API, a reference
+ *     ownership transfer happens and the user is responsible to destroy the result.
+ */
 hb_blob_t *
 hb_face_get_table (hb_face_t *face,
 		   hb_tag_t   tag);
@@ -196,7 +208,8 @@ hb_font_set_funcs (hb_font_t         *font,
  *
  * The client is responsible for:
  *
- *   - Take ownership of the reference on the returned klass
+ *   - Take ownership of the reference on the returned klass,
+ *
  *   - Calling "destroy(user_data)" exactly once if returned destroy func
  *     is not NULL and the returned info is not needed anymore.
  */
commit 645f6f265b5f6fb85b3c0f59ea874d58c86e3917
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Mon Oct 4 17:01:01 2010 -0400

    Add is_mutable() functions
    
    Correspond to the make_mutable() ones.

diff --git a/src/hb-font.cc b/src/hb-font.cc
index e257cd8..fd5df93 100644
--- a/src/hb-font.cc
+++ b/src/hb-font.cc
@@ -141,6 +141,12 @@ hb_font_funcs_make_immutable (hb_font_funcs_t *ffuncs)
   ffuncs->immutable = TRUE;
 }
 
+hb_bool_t
+hb_font_funcs_is_immutable (hb_font_funcs_t *ffuncs)
+{
+  return ffuncs->immutable = TRUE;
+}
+
 
 void
 hb_font_funcs_set_glyph_func (hb_font_funcs_t *ffuncs,
diff --git a/src/hb-font.h b/src/hb-font.h
index a7b6d8f..266b74a 100644
--- a/src/hb-font.h
+++ b/src/hb-font.h
@@ -91,6 +91,8 @@ hb_font_funcs_copy (hb_font_funcs_t *ffuncs);
 void
 hb_font_funcs_make_immutable (hb_font_funcs_t *ffuncs);
 
+hb_bool_t
+hb_font_funcs_is_immutable (hb_font_funcs_t *ffuncs);
 
 /* funcs */
 
diff --git a/src/hb-unicode.c b/src/hb-unicode.c
index 19a891b..280c61f 100644
--- a/src/hb-unicode.c
+++ b/src/hb-unicode.c
@@ -108,6 +108,12 @@ hb_unicode_funcs_make_immutable (hb_unicode_funcs_t *ufuncs)
   ufuncs->immutable = TRUE;
 }
 
+hb_bool_t
+hb_unicode_funcs_is_immutable (hb_unicode_funcs_t *ufuncs)
+{
+  return ufuncs->immutable;
+}
+
 
 void
 hb_unicode_funcs_set_mirroring_func (hb_unicode_funcs_t *ufuncs,
diff --git a/src/hb-unicode.h b/src/hb-unicode.h
index a84a948..4edab48 100644
--- a/src/hb-unicode.h
+++ b/src/hb-unicode.h
@@ -201,6 +201,8 @@ hb_unicode_funcs_copy (hb_unicode_funcs_t *ufuncs);
 void
 hb_unicode_funcs_make_immutable (hb_unicode_funcs_t *ufuncs);
 
+hb_bool_t
+hb_unicode_funcs_is_immutable (hb_unicode_funcs_t *ufuncs);
 
 /*
  * funcs
commit 19c0eab8cf96d00e168c4b11ec435019c1ed44f7
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Mon Oct 4 16:45:21 2010 -0400

    Add getters for all setter APIs
    
    One in particular is not a straight getter: hb_font_unset_funcs() is
    special because of the specific needs of the lifecycle management of
    the user_data object.

diff --git a/src/hb-font.cc b/src/hb-font.cc
index 8a547a3..e257cd8 100644
--- a/src/hb-font.cc
+++ b/src/hb-font.cc
@@ -183,6 +183,32 @@ hb_font_funcs_set_kerning_func (hb_font_funcs_t *ffuncs,
 }
 
 
+hb_font_get_glyph_func_t
+hb_font_funcs_get_glyph_func (hb_font_funcs_t *ffuncs)
+{
+  return ffuncs->v.get_glyph;
+}
+
+hb_font_get_contour_point_func_t
+hb_font_funcs_get_contour_point_func (hb_font_funcs_t *ffuncs)
+{
+  return ffuncs->v.get_contour_point;
+}
+
+hb_font_get_glyph_metrics_func_t
+hb_font_funcs_get_glyph_metrics_func (hb_font_funcs_t *ffuncs)
+{
+  return ffuncs->v.get_glyph_metrics;
+}
+
+hb_font_get_kerning_func_t
+hb_font_funcs_get_kerning_func (hb_font_funcs_t *ffuncs)
+{
+  return ffuncs->v.get_kerning;
+}
+
+
+
 hb_codepoint_t
 hb_font_get_glyph (hb_font_t *font, hb_face_t *face,
 		   hb_codepoint_t unicode, hb_codepoint_t variation_selector)
@@ -444,6 +470,26 @@ hb_font_set_funcs (hb_font_t         *font,
 }
 
 void
+hb_font_unset_funcs (hb_font_t          *font,
+		     hb_font_funcs_t   **klass,
+		     hb_destroy_func_t  *destroy,
+		     void              **user_data)
+{
+  /* None of the input arguments can be NULL. */
+
+  *klass = font->klass;
+  *destroy = font->destroy;
+  *user_data = font->user_data;
+
+  if (HB_OBJECT_IS_INERT (font))
+    return;
+
+  font->klass = NULL;
+  font->destroy = NULL;
+  font->user_data = NULL;
+}
+
+void
 hb_font_set_scale (hb_font_t *font,
 		   unsigned int x_scale,
 		   unsigned int y_scale)
@@ -456,6 +502,15 @@ hb_font_set_scale (hb_font_t *font,
 }
 
 void
+hb_font_get_scale (hb_font_t *font,
+		   unsigned int *x_scale,
+		   unsigned int *y_scale)
+{
+  if (x_scale) *x_scale = font->x_scale;
+  if (y_scale) *y_scale = font->y_scale;
+}
+
+void
 hb_font_set_ppem (hb_font_t *font,
 		  unsigned int x_ppem,
 		  unsigned int y_ppem)
@@ -467,5 +522,14 @@ hb_font_set_ppem (hb_font_t *font,
   font->y_ppem = y_ppem;
 }
 
+void
+hb_font_get_ppem (hb_font_t *font,
+		  unsigned int *x_ppem,
+		  unsigned int *y_ppem)
+{
+  if (x_ppem) *x_ppem = font->x_ppem;
+  if (y_ppem) *y_ppem = font->y_ppem;
+}
+
 
 HB_END_DECLS
diff --git a/src/hb-font.h b/src/hb-font.h
index 6f1f3ca..a7b6d8f 100644
--- a/src/hb-font.h
+++ b/src/hb-font.h
@@ -132,6 +132,21 @@ hb_font_funcs_set_kerning_func (hb_font_funcs_t *ffuncs,
 				hb_font_get_kerning_func_t kerning_func);
 
 
+/* These never return NULL.  Return fallback defaults instead. */
+
+hb_font_get_glyph_func_t
+hb_font_funcs_get_glyph_func (hb_font_funcs_t *ffuncs);
+
+hb_font_get_contour_point_func_t
+hb_font_funcs_get_contour_point_func (hb_font_funcs_t *ffuncs);
+
+hb_font_get_glyph_metrics_func_t
+hb_font_funcs_get_glyph_metrics_func (hb_font_funcs_t *ffuncs);
+
+hb_font_get_kerning_func_t
+hb_font_funcs_get_kerning_func (hb_font_funcs_t *ffuncs);
+
+
 hb_codepoint_t
 hb_font_get_glyph (hb_font_t *font, hb_face_t *face,
 		   hb_codepoint_t unicode, hb_codepoint_t variation_selector);
@@ -174,6 +189,21 @@ hb_font_set_funcs (hb_font_t         *font,
 		   hb_destroy_func_t  destroy,
 		   void              *user_data);
 
+/* Returns what was set and unsets it, but doesn't destroy(user_data).
+ * This is useful for wrapping / chaining font_funcs_t's.
+ *
+ * The client is responsible for:
+ *
+ *   - Take ownership of the reference on the returned klass
+ *   - Calling "destroy(user_data)" exactly once if returned destroy func
+ *     is not NULL and the returned info is not needed anymore.
+ */
+void
+hb_font_unset_funcs (hb_font_t          *font,
+		     hb_font_funcs_t   **klass,
+		     hb_destroy_func_t  *destroy,
+		     void              **user_data);
+
 
 /*
  * We should add support for full matrices.
@@ -183,6 +213,11 @@ hb_font_set_scale (hb_font_t *font,
 		   unsigned int x_scale,
 		   unsigned int y_scale);
 
+void
+hb_font_get_scale (hb_font_t *font,
+		   unsigned int *x_scale,
+		   unsigned int *y_scale);
+
 /*
  * A zero value means "no hinting in that direction"
  */
@@ -191,6 +226,11 @@ hb_font_set_ppem (hb_font_t *font,
 		  unsigned int x_ppem,
 		  unsigned int y_ppem);
 
+void
+hb_font_get_ppem (hb_font_t *font,
+		  unsigned int *x_ppem,
+		  unsigned int *y_ppem);
+
 
 HB_END_DECLS
 
diff --git a/src/hb-unicode.c b/src/hb-unicode.c
index 2de963e..19a891b 100644
--- a/src/hb-unicode.c
+++ b/src/hb-unicode.c
@@ -160,6 +160,38 @@ hb_unicode_funcs_set_eastasian_width_func (hb_unicode_funcs_t *ufuncs,
 }
 
 
+hb_unicode_get_mirroring_func_t
+hb_unicode_funcs_get_mirroring_func (hb_unicode_funcs_t *ufuncs)
+{
+  return ufuncs->v.get_mirroring;
+}
+
+hb_unicode_get_general_category_func_t
+hb_unicode_funcs_get_general_category_func (hb_unicode_funcs_t *ufuncs)
+{
+  return ufuncs->v.get_general_category;
+}
+
+hb_unicode_get_script_func_t
+hb_unicode_funcs_get_script_func (hb_unicode_funcs_t *ufuncs)
+{
+  return ufuncs->v.get_script;
+}
+
+hb_unicode_get_combining_class_func_t
+hb_unicode_funcs_get_combining_class_func (hb_unicode_funcs_t *ufuncs)
+{
+  return ufuncs->v.get_combining_class;
+}
+
+hb_unicode_get_eastasian_width_func_t
+hb_unicode_funcs_get_eastasian_width_func (hb_unicode_funcs_t *ufuncs)
+{
+  return ufuncs->v.get_eastasian_width;
+}
+
+
+
 hb_codepoint_t
 hb_unicode_get_mirroring (hb_unicode_funcs_t *ufuncs,
 			  hb_codepoint_t unicode)
diff --git a/src/hb-unicode.h b/src/hb-unicode.h
index 8aeba33..a84a948 100644
--- a/src/hb-unicode.h
+++ b/src/hb-unicode.h
@@ -202,7 +202,12 @@ void
 hb_unicode_funcs_make_immutable (hb_unicode_funcs_t *ufuncs);
 
 
-/* funcs */
+/*
+ * funcs
+ */
+
+
+/* typedefs */
 
 typedef hb_codepoint_t (*hb_unicode_get_mirroring_func_t) (hb_codepoint_t unicode);
 typedef hb_category_t (*hb_unicode_get_general_category_func_t) (hb_codepoint_t unicode);
@@ -211,6 +216,8 @@ typedef unsigned int (*hb_unicode_get_combining_class_func_t) (hb_codepoint_t un
 typedef unsigned int (*hb_unicode_get_eastasian_width_func_t) (hb_codepoint_t unicode);
 
 
+/* setters */
+
 void
 hb_unicode_funcs_set_mirroring_func (hb_unicode_funcs_t *ufuncs,
 				     hb_unicode_get_mirroring_func_t mirroring_func);
@@ -232,6 +239,28 @@ hb_unicode_funcs_set_eastasian_width_func (hb_unicode_funcs_t *ufuncs,
 					   hb_unicode_get_eastasian_width_func_t eastasian_width_func);
 
 
+/* getters */
+
+/* These never return NULL.  Return fallback defaults instead. */
+
+hb_unicode_get_mirroring_func_t
+hb_unicode_funcs_get_mirroring_func (hb_unicode_funcs_t *ufuncs);
+
+hb_unicode_get_general_category_func_t
+hb_unicode_funcs_get_general_category_func (hb_unicode_funcs_t *ufuncs);
+
+hb_unicode_get_script_func_t
+hb_unicode_funcs_get_script_func (hb_unicode_funcs_t *ufuncs);
+
+hb_unicode_get_combining_class_func_t
+hb_unicode_funcs_get_combining_class_func (hb_unicode_funcs_t *ufuncs);
+
+hb_unicode_get_eastasian_width_func_t
+hb_unicode_funcs_get_eastasian_width_func (hb_unicode_funcs_t *ufuncs);
+
+
+/* accessors */
+
 hb_codepoint_t
 hb_unicode_get_mirroring (hb_unicode_funcs_t *ufuncs,
 			  hb_codepoint_t unicode);
commit f0feb084b0fd1510474b25404d1dcc5686ee0538
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Sun Oct 3 19:09:39 2010 -0400

    Minor

diff --git a/src/hb-ot-layout-private.hh b/src/hb-ot-layout-private.hh
index c1ae99f..fbaff13 100644
--- a/src/hb-ot-layout-private.hh
+++ b/src/hb-ot-layout-private.hh
@@ -70,16 +70,18 @@ struct hb_ot_layout_context_t
   {
     struct gpos_t
     {
-      unsigned int last;        /* the last valid glyph--used with cursive positioning */
+      unsigned int last;        /* the last matched glyph--used with cursive positioning */
       hb_position_t anchor_x;   /* the coordinates of the anchor point */
-      hb_position_t anchor_y;   /* of the last valid glyph */
+      hb_position_t anchor_y;   /* of the last matched glyph */
     } gpos;
   } info;
 
   /* Convert from font-space to user-space */
-  /* XXX speed up */
-  inline hb_position_t scale_x (int16_t v) { return (int64_t) this->font->x_scale * v / this->face->head_table->get_upem (); }
-  inline hb_position_t scale_y (int16_t v) { return (int64_t) this->font->y_scale * v / this->face->head_table->get_upem (); }
+  inline hb_position_t scale_x (int16_t v) { return scale (v, this->font->x_scale); }
+  inline hb_position_t scale_y (int16_t v) { return scale (v, this->font->y_scale); }
+
+  private:
+  inline hb_position_t scale (int16_t v, unsigned int scale) { return v * (int64_t) scale / this->face->head_table->get_upem (); }
 };
 
 



More information about the HarfBuzz mailing list