[HarfBuzz] harfbuzz: Branch 'master'

Behdad Esfahbod behdad at kemper.freedesktop.org
Wed Sep 5 21:51:57 PDT 2012


 src/Makefile.am                            |    1 
 src/hb-open-type-private.hh                |   69 +++++--
 src/hb-ot-layout-common-private.hh         |    9 -
 src/hb-ot-layout-gsub-table.hh             |   11 -
 src/hb-ot-map-private.hh                   |    6 
 src/hb-ot-map.cc                           |    1 
 src/hb-ot-shape-complex-arabic-fallback.hh |  252 +++++++++++++++++++++++++++++
 src/hb-ot-shape-complex-arabic.cc          |  158 +++++-------------
 src/hb-ot-shape.cc                         |    7 
 src/hb-private.hh                          |   11 -
 10 files changed, 369 insertions(+), 156 deletions(-)

New commits:
commit fabd3113a98c5f4114f48920fa7ea38bd65a8d32
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Wed Sep 5 22:19:28 2012 -0400

    [OT] Port Arabic fallback shaping to synthetic GSUB
    
    All of init/medi/fina/isol and rlig implemented.
    
    Let there be dragons... ⻯

diff --git a/src/Makefile.am b/src/Makefile.am
index d7c4560..609635c 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -78,6 +78,7 @@ HBSOURCES += \
 	hb-ot-map-private.hh \
 	hb-ot-shape.cc \
 	hb-ot-shape-complex-arabic.cc \
+	hb-ot-shape-complex-arabic-fallback.hh \
 	hb-ot-shape-complex-arabic-table.hh \
 	hb-ot-shape-complex-indic.cc \
 	hb-ot-shape-complex-indic-machine.hh \
diff --git a/src/hb-open-type-private.hh b/src/hb-open-type-private.hh
index c273a97..7106a2e 100644
--- a/src/hb-open-type-private.hh
+++ b/src/hb-open-type-private.hh
@@ -342,38 +342,52 @@ struct Sanitizer
 
 struct hb_serialize_context_t
 {
-  inline void init (void *start, unsigned int size)
+  inline hb_serialize_context_t (void *start, unsigned int size)
   {
     this->start = (char *) start;
     this->end = this->start + size;
-  }
 
-  inline void start_processing (void)
-  {
     this->ran_out_of_room = false;
     this->head = this->start;
     this->debug_depth = 0;
+  }
 
+  template <typename Type>
+  inline Type *start_serialize (void)
+  {
     DEBUG_MSG_LEVEL (SERIALIZE, this->start, 0, +1,
 		     "start [%p..%p] (%lu bytes)",
 		     this->start, this->end,
 		     (unsigned long) (this->end - this->start));
+
+    return start_embed<Type> ();
   }
 
-  inline void end_processing (void)
+  inline void end_serialize (void)
   {
     DEBUG_MSG_LEVEL (SERIALIZE, this->start, 0, -1,
-		     "end [%p..%p] %s",
+		     "end [%p..%p] serialized %d bytes; %s",
 		     this->start, this->end,
+		     (int) (this->head - this->start),
 		     this->ran_out_of_room ? "RAN OUT OF ROOM" : "did not ran out of room");
 
-    this->start = this->end = this->head = NULL;
+  }
+
+  template <typename Type>
+  inline Type *copy (void)
+  {
+    assert (!this->ran_out_of_room);
+    unsigned int len = this->head - this->start;
+    void *p = malloc (len);
+    if (p)
+      memcpy (p, this->start, len);
+    return reinterpret_cast<Type *> (p);
   }
 
   template <typename Type>
   inline Type *allocate_size (unsigned int size)
   {
-    if (unlikely (this->ran_out_of_room || this->end - this->head > size)) {
+    if (unlikely (this->ran_out_of_room || this->end - this->head < size)) {
       this->ran_out_of_room = true;
       return NULL;
     }
@@ -390,6 +404,13 @@ struct hb_serialize_context_t
   }
 
   template <typename Type>
+  inline Type *start_embed (void)
+  {
+    Type *ret = reinterpret_cast<Type *> (this->head);
+    return ret;
+  }
+
+  template <typename Type>
   inline Type *embed (const Type &obj)
   {
     unsigned int size = obj.get_size ();
@@ -403,8 +424,8 @@ struct hb_serialize_context_t
   inline Type *extend_min (Type &obj)
   {
     unsigned int size = obj.min_size;
-    assert (this->start < (char *) &obj && (char *) &obj <= this->head && (char *) &obj + size >= this->head);
-    this->allocate_size<Type> (((char *) &obj) + size - this->head);
+    assert (this->start <= (char *) &obj && (char *) &obj <= this->head && (char *) &obj + size >= this->head);
+    if (unlikely (!this->allocate_size<Type> (((char *) &obj) + size - this->head))) return NULL;
     return reinterpret_cast<Type *> (&obj);
   }
 
@@ -413,7 +434,7 @@ struct hb_serialize_context_t
   {
     unsigned int size = obj.get_size ();
     assert (this->start < (char *) &obj && (char *) &obj <= this->head && (char *) &obj + size >= this->head);
-    this->allocate_size<Type> (((char *) &obj) + size - this->head);
+    if (unlikely (!this->allocate_size<Type> (((char *) &obj) + size - this->head))) return false;
     return reinterpret_cast<Type *> (&obj);
   }
 
@@ -431,13 +452,6 @@ struct hb_serialize_context_t
 template <typename Type>
 struct Supplier
 {
-  /* For automatic wrapping of bare arrays */
-  inline Supplier (const Type *array)
-  {
-    head = array;
-    len = (unsigned int) -1;
-  }
-
   inline Supplier (const Type *array, unsigned int len_)
   {
     head = array;
@@ -449,7 +463,8 @@ struct Supplier
     return head[i];
   }
 
-  inline void advance (unsigned int count) {
+  inline void advance (unsigned int count)
+  {
     if (unlikely (count > len))
       count = len;
     len -= count;
@@ -457,6 +472,9 @@ struct Supplier
   }
 
   private:
+  inline Supplier (const Supplier<Type> &); /* Disallow copy */
+  inline Supplier<Type>& operator= (const Supplier<Type> &); /* Disallow copy */
+
   unsigned int len;
   const Type *head;
 };
@@ -509,6 +527,8 @@ struct IntType
   inline operator Type(void) const { return v; }
   inline bool operator == (const IntType<Type> &o) const { return v == o.v; }
   inline bool operator != (const IntType<Type> &o) const { return v != o.v; }
+  static inline int cmp (const IntType<Type> *a, const IntType<Type> *b) { return b->cmp (*a); }
+  inline int cmp (IntType<Type> va) const { Type a = va; Type b = v; return a < b ? -1 : a == b ? 0 : +1; }
   inline int cmp (Type a) const { Type b = v; return a < b ? -1 : a == b ? 0 : +1; }
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
@@ -634,7 +654,7 @@ struct GenericOffsetTo : OffsetType
 
   inline Type& serialize (hb_serialize_context_t *c, void *base)
   {
-    Type *t = (Type *) c->head;
+    Type *t = c->start_embed<Type> ();
     this->set ((char *) t - (char *) base); /* TODO(serialize) Overflow? */
     return *t;
   }
@@ -727,7 +747,7 @@ struct GenericArrayOf
     TRACE_SERIALIZE ();
     if (unlikely (!serialize (c, items_len))) return TRACE_RETURN (false);
     for (unsigned int i = 0; i < items_len; i++)
-      array[i].set (items[i]);
+      array[i] = items[i];
     items.advance (items_len);
     return TRACE_RETURN (true);
   }
@@ -842,11 +862,10 @@ struct HeadlessArrayOf
     TRACE_SERIALIZE ();
     if (unlikely (!c->extend_min (*this))) return TRACE_RETURN (false);
     len.set (items_len); /* TODO(serialize) Overflow? */
-    if (unlikely (!c->extend (*this))) return TRACE_RETURN (false);
     if (unlikely (!items_len)) return TRACE_RETURN (true);
-    unsigned int count = items_len;
-    for (unsigned int i = 1; i < count; i++)
-      array[i-1].set (items[i]);
+    if (unlikely (!c->extend (*this))) return TRACE_RETURN (false);
+    for (unsigned int i = 0; i < items_len - 1; i++)
+      array[i] = items[i];
     items.advance (items_len - 1);
     return TRACE_RETURN (true);
   }
diff --git a/src/hb-ot-layout-common-private.hh b/src/hb-ot-layout-common-private.hh
index 5c51ee5..f5a067a 100644
--- a/src/hb-ot-layout-common-private.hh
+++ b/src/hb-ot-layout-common-private.hh
@@ -382,7 +382,7 @@ struct CoverageFormat1
     glyphArray.len.set (num_glyphs);
     if (unlikely (!c->extend (glyphArray))) return TRACE_RETURN (false);
     for (unsigned int i = 0; i < num_glyphs; i++)
-      glyphArray[i].set (glyphs[i]);
+      glyphArray[i] = glyphs[i];
     glyphs.advance (num_glyphs);
     return TRACE_RETURN (true);
   }
@@ -455,13 +455,14 @@ struct CoverageFormat2
     if (unlikely (!c->extend (rangeRecord))) return TRACE_RETURN (false);
 
     unsigned int range = 0;
-    rangeRecord[range].start.set (glyphs[0]);
+    rangeRecord[range].start = glyphs[0];
     rangeRecord[range].value.set (0);
     for (unsigned int i = 1; i < num_glyphs; i++)
       if (glyphs[i - 1] + 1 != glyphs[i]) {
-	rangeRecord[range].start.set (glyphs[i]);
-	rangeRecord[range].value.set (i);
 	range++;
+	rangeRecord[range].start = glyphs[i];
+	rangeRecord[range].value.set (i);
+        rangeRecord[range].end = glyphs[i];
       } else {
         rangeRecord[range].end = glyphs[i];
       }
diff --git a/src/hb-ot-layout-gsub-table.hh b/src/hb-ot-layout-gsub-table.hh
index bbd7214..90faa79 100644
--- a/src/hb-ot-layout-gsub-table.hh
+++ b/src/hb-ot-layout-gsub-table.hh
@@ -143,8 +143,8 @@ struct SingleSubstFormat2
   {
     TRACE_SERIALIZE ();
     if (unlikely (!c->extend_min (*this))) return TRACE_RETURN (false);
-    if (unlikely (!coverage.serialize (c, this).serialize (c, glyphs, num_glyphs))) return TRACE_RETURN (false);
     if (unlikely (!substitute.serialize (c, substitutes, num_glyphs))) return TRACE_RETURN (false);
+    if (unlikely (!coverage.serialize (c, this).serialize (c, glyphs, num_glyphs))) return TRACE_RETURN (false);
     return TRACE_RETURN (true);
   }
 
@@ -339,13 +339,13 @@ struct MultipleSubstFormat1
   {
     TRACE_SERIALIZE ();
     if (unlikely (!c->extend_min (*this))) return TRACE_RETURN (false);
-    if (unlikely (!coverage.serialize (c, this).serialize (c, glyphs, num_glyphs))) return TRACE_RETURN (false);
     if (unlikely (!sequence.serialize (c, num_glyphs))) return TRACE_RETURN (false);
     for (unsigned int i = 0; i < num_glyphs; i++)
       if (unlikely (!sequence[i].serialize (c, this).serialize (c,
 								substitute_glyphs_list,
 								substitute_len_list[i]))) return TRACE_RETURN (false);
     substitute_len_list.advance (num_glyphs);
+    if (unlikely (!coverage.serialize (c, this).serialize (c, glyphs, num_glyphs))) return TRACE_RETURN (false);
     return TRACE_RETURN (true);
   }
 
@@ -496,13 +496,13 @@ struct AlternateSubstFormat1
   {
     TRACE_SERIALIZE ();
     if (unlikely (!c->extend_min (*this))) return TRACE_RETURN (false);
-    if (unlikely (!coverage.serialize (c, this).serialize (c, glyphs, num_glyphs))) return TRACE_RETURN (false);
     if (unlikely (!alternateSet.serialize (c, num_glyphs))) return TRACE_RETURN (false);
     for (unsigned int i = 0; i < num_glyphs; i++)
       if (unlikely (!alternateSet[i].serialize (c, this).serialize (c,
 								    alternate_glyphs_list,
 								    alternate_len_list[i]))) return TRACE_RETURN (false);
     alternate_len_list.advance (num_glyphs);
+    if (unlikely (!coverage.serialize (c, this).serialize (c, glyphs, num_glyphs))) return TRACE_RETURN (false);
     return TRACE_RETURN (true);
   }
 
@@ -658,7 +658,7 @@ struct Ligature
   {
     TRACE_SERIALIZE ();
     if (unlikely (!c->extend_min (*this))) return TRACE_RETURN (false);
-    ligGlyph.set (ligature);
+    ligGlyph = ligature;
     if (unlikely (!component.serialize (c, components, num_components))) return TRACE_RETURN (false);
     return TRACE_RETURN (true);
   }
@@ -732,6 +732,7 @@ struct LigatureSet
 								ligatures[i],
 								component_list,
 								component_count_list[i]))) return TRACE_RETURN (false);
+    ligatures.advance (num_ligatures);
     component_count_list.advance (num_ligatures);
     return TRACE_RETURN (true);
   }
@@ -798,7 +799,6 @@ struct LigatureSubstFormat1
   {
     TRACE_SERIALIZE ();
     if (unlikely (!c->extend_min (*this))) return TRACE_RETURN (false);
-    if (unlikely (!coverage.serialize (c, this).serialize (c, first_glyphs, num_first_glyphs))) return TRACE_RETURN (false);
     if (unlikely (!ligatureSet.serialize (c, num_first_glyphs))) return TRACE_RETURN (false);
     for (unsigned int i = 0; i < num_first_glyphs; i++)
       if (unlikely (!ligatureSet[i].serialize (c, this).serialize (c,
@@ -807,6 +807,7 @@ struct LigatureSubstFormat1
 								   ligature_per_first_glyph_count_list[i],
 								   component_list))) return TRACE_RETURN (false);
     ligature_per_first_glyph_count_list.advance (num_first_glyphs);
+    if (unlikely (!coverage.serialize (c, this).serialize (c, first_glyphs, num_first_glyphs))) return TRACE_RETURN (false);
     return TRACE_RETURN (true);
   }
 
diff --git a/src/hb-ot-map-private.hh b/src/hb-ot-map-private.hh
index 22ea8ba..7eb85c8 100644
--- a/src/hb-ot-map-private.hh
+++ b/src/hb-ot-map-private.hh
@@ -49,6 +49,7 @@ struct hb_ot_map_t
     unsigned int shift;
     hb_mask_t mask;
     hb_mask_t _1_mask; /* mask for value=1, for quick access */
+    hb_bool_t needs_fallback;
 
     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; }
@@ -80,6 +81,11 @@ struct hb_ot_map_t
     return map ? map->mask : 0;
   }
 
+  inline bool needs_fallback (hb_tag_t feature_tag) const {
+    const feature_map_t *map = features.bsearch (&feature_tag);
+    return map ? map->needs_fallback : false;
+  }
+
   inline hb_mask_t get_1_mask (hb_tag_t feature_tag) const {
     const feature_map_t *map = features.bsearch (&feature_tag);
     return map ? map->_1_mask : 0;
diff --git a/src/hb-ot-map.cc b/src/hb-ot-map.cc
index aae4d03..f290c98 100644
--- a/src/hb-ot-map.cc
+++ b/src/hb-ot-map.cc
@@ -236,6 +236,7 @@ hb_ot_map_builder_t::compile (hb_face_t *face,
 	m.global_mask |= (info->default_value << map->shift) & map->mask;
     }
     map->_1_mask = (1 << map->shift) & map->mask;
+    map->needs_fallback = !found;
 
   }
   feature_infos.shrink (0); /* Done with these */
diff --git a/src/hb-ot-shape-complex-arabic-fallback.hh b/src/hb-ot-shape-complex-arabic-fallback.hh
new file mode 100644
index 0000000..c9e89a6
--- /dev/null
+++ b/src/hb-ot-shape-complex-arabic-fallback.hh
@@ -0,0 +1,252 @@
+/*
+ * Copyright © 2012  Google, Inc.
+ *
+ *  This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Google Author(s): Behdad Esfahbod
+ */
+
+#ifndef HB_OT_SHAPE_COMPLEX_ARABIC_FALLBACK_HH
+#define HB_OT_SHAPE_COMPLEX_ARABIC_FALLBACK_HH
+
+#include "hb-private.hh"
+
+#include "hb-ot-shape-private.hh"
+#include "hb-ot-layout-gsub-table.hh"
+
+
+static const hb_tag_t arabic_fallback_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'),
+  HB_TAG('r','l','i','g'),
+};
+
+/* Same order as the fallback feature array */
+enum {
+  FALLBACK_INIT,
+  FALLBACK_MEDI,
+  FALLBACK_FINA,
+  FALLBACK_ISOL,
+  FALLBACK_RLIG,
+  ARABIC_NUM_FALLBACK_FEATURES
+};
+
+static OT::SubstLookup *
+arabic_fallback_synthesize_lookup_single (const hb_ot_shape_plan_t *plan,
+					  hb_font_t *font,
+					  unsigned int feature_index)
+{
+  OT::GlyphID glyphs[SHAPING_TABLE_LAST - SHAPING_TABLE_FIRST + 1];
+  OT::GlyphID substitutes[SHAPING_TABLE_LAST - SHAPING_TABLE_FIRST + 1];
+  unsigned int num_glyphs = 0;
+
+  /* Populate arrays */
+  for (hb_codepoint_t u = SHAPING_TABLE_FIRST; u < SHAPING_TABLE_LAST + 1; u++)
+  {
+    hb_codepoint_t s = shaping_table[u - SHAPING_TABLE_FIRST][feature_index];
+    hb_codepoint_t u_glyph, s_glyph;
+
+    if (u == s ||
+	!hb_font_get_glyph (font, u, 0, &u_glyph) ||
+	!hb_font_get_glyph (font, s, 0, &s_glyph) ||
+	u_glyph == s_glyph ||
+	u_glyph > 0xFFFF || s_glyph > 0xFFFF)
+      continue;
+
+    glyphs[num_glyphs].set (u_glyph);
+    substitutes[num_glyphs].set (s_glyph);
+
+    num_glyphs++;
+  }
+
+  /* Bubble-sort!
+   * May not be good-enough for presidential candidate interviews, but good-enough for us... */
+  hb_bubble_sort (&glyphs[0], num_glyphs, OT::GlyphID::cmp, &substitutes[0]);
+
+  OT::Supplier<OT::GlyphID> glyphs_supplier      (glyphs, num_glyphs);
+  OT::Supplier<OT::GlyphID> substitutes_supplier (substitutes, num_glyphs);
+
+  /* Each glyph takes four bytes max, and there's some overhead. */
+  char buf[(SHAPING_TABLE_LAST - SHAPING_TABLE_FIRST + 1) * 4 + 128];
+  OT::hb_serialize_context_t c (buf, sizeof (buf));
+  OT::SubstLookup *lookup = c.start_serialize<OT::SubstLookup> ();
+  bool ret = lookup->serialize_single (&c,
+				       OT::LookupFlag::IgnoreMarks,
+				       glyphs_supplier,
+				       substitutes_supplier,
+				       num_glyphs);
+  c.end_serialize ();
+  /* TODO sanitize the results? */
+
+  return ret ? c.copy<OT::SubstLookup> () : NULL;
+}
+
+static OT::SubstLookup *
+arabic_fallback_synthesize_lookup_ligature (const hb_ot_shape_plan_t *plan,
+					    hb_font_t *font)
+{
+  OT::GlyphID first_glyphs[ARRAY_LENGTH_CONST (ligature_table)];
+  unsigned int first_glyphs_indirection[ARRAY_LENGTH_CONST (ligature_table)];
+  unsigned int ligature_per_first_glyph_count_list[ARRAY_LENGTH_CONST (first_glyphs)];
+  unsigned int num_first_glyphs = 0;
+
+  /* We know that all our ligatures are 2-component */
+  OT::GlyphID ligatures_list[ARRAY_LENGTH_CONST (first_glyphs) * ARRAY_LENGTH_CONST(ligature_table[0].ligatures)];
+  unsigned int component_count_list[ARRAY_LENGTH_CONST (ligatures_list)];
+  OT::GlyphID component_list[ARRAY_LENGTH_CONST (ligatures_list) * 1/* One extra component per ligature */];
+  unsigned int num_ligatures = 0;
+
+  /* Populate arrays */
+
+  /* Sort out the first-glyphs */
+  for (unsigned int first_glyph_idx = 0; first_glyph_idx < ARRAY_LENGTH (first_glyphs); first_glyph_idx++)
+  {
+    hb_codepoint_t first_u = ligature_table[first_glyph_idx].first;
+    hb_codepoint_t first_glyph;
+    if (!hb_font_get_glyph (font, first_u, 0, &first_glyph))
+      continue;
+    first_glyphs[num_first_glyphs].set (first_glyph);
+    ligature_per_first_glyph_count_list[num_first_glyphs] = 0;
+    first_glyphs_indirection[num_first_glyphs] = first_glyph_idx;
+    num_first_glyphs++;
+  }
+  hb_bubble_sort (&first_glyphs[0], num_first_glyphs, OT::GlyphID::cmp, &first_glyphs_indirection[0]);
+
+  /* Now that the first-glyphs are sorted, walk again, populate ligatures. */
+  for (unsigned int i = 0; i < num_first_glyphs; i++)
+  {
+    unsigned int first_glyph_idx = first_glyphs_indirection[i];
+
+    for (unsigned int second_glyph_idx = 0; second_glyph_idx < ARRAY_LENGTH (ligature_table[0].ligatures); second_glyph_idx++)
+    {
+      hb_codepoint_t second_u   = ligature_table[first_glyph_idx].ligatures[second_glyph_idx].second;
+      hb_codepoint_t ligature_u = ligature_table[first_glyph_idx].ligatures[second_glyph_idx].ligature;
+      hb_codepoint_t second_glyph, ligature_glyph;
+      if (!second_u ||
+	  !hb_font_get_glyph (font, second_u,   0, &second_glyph) ||
+	  !hb_font_get_glyph (font, ligature_u, 0, &ligature_glyph))
+	continue;
+
+      ligature_per_first_glyph_count_list[i]++;
+
+      ligatures_list[num_ligatures].set (ligature_glyph);
+      component_count_list[num_ligatures] = 2;
+      component_list[num_ligatures].set (second_glyph);
+      num_ligatures++;
+    }
+  }
+
+  OT::Supplier<OT::GlyphID>   first_glyphs_supplier                      (first_glyphs, num_first_glyphs);
+  OT::Supplier<unsigned int > ligature_per_first_glyph_count_supplier    (ligature_per_first_glyph_count_list, num_first_glyphs);
+  OT::Supplier<OT::GlyphID>   ligatures_supplier                         (ligatures_list, num_ligatures);
+  OT::Supplier<unsigned int > component_count_supplier                   (component_count_list, num_ligatures);
+  OT::Supplier<OT::GlyphID>   component_supplier                         (component_list, num_ligatures);
+
+  char buf[2048];
+  OT::hb_serialize_context_t c (buf, sizeof (buf));
+  OT::SubstLookup *lookup = c.start_serialize<OT::SubstLookup> ();
+  bool ret = lookup->serialize_ligature (&c,
+					 OT::LookupFlag::IgnoreMarks,
+					 first_glyphs_supplier,
+					 ligature_per_first_glyph_count_supplier,
+					 num_first_glyphs,
+					 ligatures_supplier,
+					 component_count_supplier,
+					 component_supplier);
+
+  c.end_serialize ();
+  /* TODO sanitize the results? */
+
+  return ret ? c.copy<OT::SubstLookup> () : NULL;
+}
+
+static OT::SubstLookup *
+arabic_fallback_synthesize_lookup (const hb_ot_shape_plan_t *plan,
+				   hb_font_t *font,
+				   unsigned int feature_index)
+{
+  if (feature_index < 4)
+    return arabic_fallback_synthesize_lookup_single (plan, font, feature_index);
+  else
+    return arabic_fallback_synthesize_lookup_ligature (plan, font);
+}
+
+struct arabic_fallback_plan_t
+{
+  ASSERT_POD ();
+
+  hb_mask_t mask_array[ARABIC_NUM_FALLBACK_FEATURES];
+  OT::SubstLookup *lookup_array[ARABIC_NUM_FALLBACK_FEATURES];
+  hb_set_digest_t digest_array[ARABIC_NUM_FALLBACK_FEATURES];
+};
+
+static const arabic_fallback_plan_t arabic_fallback_plan_nil = {};
+
+static arabic_fallback_plan_t *
+arabic_fallback_plan_create (const hb_ot_shape_plan_t *plan,
+			     hb_font_t *font)
+{
+  arabic_fallback_plan_t *fallback_plan = (arabic_fallback_plan_t *) calloc (1, sizeof (arabic_fallback_plan_t));
+  if (unlikely (!fallback_plan))
+    return const_cast<arabic_fallback_plan_t *> (&arabic_fallback_plan_nil);
+
+  for (unsigned int i = 0; i < ARABIC_NUM_FALLBACK_FEATURES; i++) {
+    fallback_plan->mask_array[i] = plan->map.get_1_mask (arabic_fallback_features[i]);
+    if (fallback_plan->mask_array[i]) {
+      fallback_plan->lookup_array[i] = arabic_fallback_synthesize_lookup (plan, font, i);
+      if (fallback_plan->lookup_array[i])
+	fallback_plan->lookup_array[i]->add_coverage (&fallback_plan->digest_array[i]);
+    }
+  }
+
+  return fallback_plan;
+}
+
+static void
+arabic_fallback_plan_destroy (arabic_fallback_plan_t *fallback_plan)
+{
+  if (!fallback_plan || fallback_plan == &arabic_fallback_plan_nil)
+    return;
+
+  for (unsigned int i = 0; i < ARABIC_NUM_FALLBACK_FEATURES; i++)
+    if (fallback_plan->lookup_array[i])
+      free (fallback_plan->lookup_array[i]);
+
+  free (fallback_plan);
+}
+
+static void
+arabic_fallback_plan_shape (arabic_fallback_plan_t *fallback_plan,
+			    hb_font_t *font,
+			    hb_buffer_t *buffer)
+{
+  for (unsigned int i = 0; i < ARABIC_NUM_FALLBACK_FEATURES; i++)
+    if (fallback_plan->lookup_array[i]) {
+      OT::hb_apply_context_t c (font, buffer, fallback_plan->mask_array[i]);
+      fallback_plan->lookup_array[i]->apply_string (&c, &fallback_plan->digest_array[i]);
+    }
+}
+
+
+#endif /* HB_OT_SHAPE_COMPLEX_ARABIC_FALLBACK_HH */
diff --git a/src/hb-ot-shape-complex-arabic.cc b/src/hb-ot-shape-complex-arabic.cc
index ba9129b..56949dd 100644
--- a/src/hb-ot-shape-complex-arabic.cc
+++ b/src/hb-ot-shape-complex-arabic.cc
@@ -26,7 +26,6 @@
 
 #include "hb-ot-shape-complex-private.hh"
 #include "hb-ot-shape-private.hh"
-#include "hb-ot-layout-gsubgpos-private.hh"
 
 
 /* buffer var allocations */
@@ -81,13 +80,6 @@ static unsigned int get_joining_type (hb_codepoint_t u, hb_unicode_general_categ
 	 JOINING_TYPE_T : JOINING_TYPE_U;
 }
 
-static hb_codepoint_t get_arabic_shape (hb_codepoint_t u, unsigned int shape)
-{
-  if (likely (hb_in_range<hb_codepoint_t> (u, SHAPING_TABLE_FIRST, SHAPING_TABLE_LAST)) && shape < 4)
-    return shaping_table[u - SHAPING_TABLE_FIRST][shape];
-  return u;
-}
-
 static const hb_tag_t arabic_features[] =
 {
   HB_TAG('i','n','i','t'),
@@ -150,6 +142,10 @@ static const struct arabic_state_table_entry {
 };
 
 
+static void
+arabic_fallback_shape (const hb_ot_shape_plan_t *plan,
+		       hb_font_t *font,
+		       hb_buffer_t *buffer);
 
 static void
 collect_features_arabic (hb_ot_shape_planner_t *plan)
@@ -172,12 +168,12 @@ collect_features_arabic (hb_ot_shape_planner_t *plan)
   map->add_gsub_pause (NULL);
 
   for (unsigned int i = 0; i < ARABIC_NUM_FEATURES; i++)
-    map->add_bool_feature (arabic_features[i], false);
+    map->add_bool_feature (arabic_features[i], false, i < 4); /* The first four features have fallback. */
 
   map->add_gsub_pause (NULL);
 
-  map->add_bool_feature (HB_TAG('r','l','i','g'));
-  map->add_gsub_pause (NULL);
+  map->add_bool_feature (HB_TAG('r','l','i','g'), true, true);
+  map->add_gsub_pause (arabic_fallback_shape);
 
   map->add_bool_feature (HB_TAG('c','a','l','t'));
   map->add_gsub_pause (NULL);
@@ -186,16 +182,20 @@ collect_features_arabic (hb_ot_shape_planner_t *plan)
   map->add_bool_feature (HB_TAG('c','s','w','h'));
 }
 
+#include "hb-ot-shape-complex-arabic-fallback.hh"
+
 struct arabic_shape_plan_t
 {
   ASSERT_POD ();
 
-  bool do_fallback;
   /* The "+ 1" in the next array is to accommodate for the "NONE" command,
    * which is not an OpenType feature, but this simplifies the code by not
    * having to do a "if (... < NONE) ..." and just rely on the fact that
    * mask_array[NONE] == 0. */
   hb_mask_t mask_array[ARABIC_NUM_FEATURES + 1];
+
+  bool do_fallback;
+  arabic_fallback_plan_t *fallback_plan;
 };
 
 static void *
@@ -205,102 +205,24 @@ data_create_arabic (const hb_ot_shape_plan_t *plan)
   if (unlikely (!arabic_plan))
     return NULL;
 
-  hb_mask_t total_masks = 0;
+  arabic_plan->do_fallback = plan->props.script == HB_SCRIPT_ARABIC;
   for (unsigned int i = 0; i < ARABIC_NUM_FEATURES; i++) {
     arabic_plan->mask_array[i] = plan->map.get_1_mask (arabic_features[i]);
-    total_masks |= arabic_plan->mask_array[i];
+    if (i < 4)
+      arabic_plan->do_fallback = arabic_plan->do_fallback && plan->map.needs_fallback (arabic_features[i]);
   }
 
-  /* Pitfalls:
-   * - This path fires if user force-set init/medi/fina/isol off,
-   * - If font does not declare script 'arab', well, what to do?
-   *   Most probably it's safe to assume that init/medi/fina/isol
-   *   still mean Arabic shaping, although they do not have to.
-   */
-  arabic_plan->do_fallback = 0 == total_masks;
-
   return arabic_plan;
 }
 
 static void
 data_destroy_arabic (void *data)
 {
-  free (data);
-}
-
-static void
-arabic_fallback_shape (hb_font_t *font, hb_buffer_t *buffer)
-{
-  /* Only Arabic has presentation forms encoded in Unicode. */
-  if (buffer->props.script != HB_SCRIPT_ARABIC)
-    return;
-
-  unsigned int count = buffer->len;
-  hb_codepoint_t glyph;
-
-  /* Shape to presentation forms */
-  for (unsigned int i = 0; i < count; i++) {
-    hb_codepoint_t u = buffer->info[i].codepoint;
-    hb_codepoint_t shaped = get_arabic_shape (u, buffer->info[i].arabic_shaping_action());
-    if (shaped != u && font->get_glyph (shaped, 0, &glyph))
-      buffer->info[i].codepoint = shaped;
-  }
+  arabic_shape_plan_t *arabic_plan = (arabic_shape_plan_t *) data;
 
-  OT::hb_apply_context_t c (font, buffer, 1/*global mask*/);
-  c.set_lookup_props (OT::LookupFlag::IgnoreMarks);
+  arabic_fallback_plan_destroy (arabic_plan->fallback_plan);
 
-  /* Mandatory ligatures */
-  buffer->clear_output ();
-  for (buffer->idx = 0; buffer->idx + 1 < count;)
-  {
-    const unsigned int count = 2;
-    unsigned int end_offset;
-    bool is_mark_ligature;
-    unsigned int total_component_count;
-
-    bool matched = false;
-    for (unsigned i = 0; i < ARRAY_LENGTH (ligature_table); i++)
-    {
-      if (ligature_table[i].first != buffer->cur().codepoint)
-        continue;
-      for (unsigned j = 0; j < ARRAY_LENGTH (ligature_table[i].ligatures); j++)
-      {
-	OT::USHORT component;
-	component.set (ligature_table[i].ligatures[j].second);
-	hb_codepoint_t ligature = ligature_table[i].ligatures[j].ligature;
-	if (likely (!OT::match_input (&c, count,
-				      &component,
-				      OT::match_glyph,
-				      NULL,
-				      &end_offset,
-				      &is_mark_ligature,
-				      &total_component_count) ||
-		    !(font->get_glyph (ligature, 0, &glyph))))
-	  continue;
-
-	/* Deal, we are forming the ligature. */
-	buffer->merge_clusters (buffer->idx, buffer->idx + end_offset);
-
-	OT::ligate_input (&c,
-			  count,
-			  &component,
-			  ligature,
-			  OT::match_glyph,
-			  NULL,
-			  is_mark_ligature,
-			  total_component_count);
-	matched = true;
-	break;
-      }
-      if (matched)
-        break;
-    }
-    if (!matched)
-      buffer->next_glyph ();
-  }
-  for (; buffer->idx < count;)
-      buffer->next_glyph ();
-  buffer->swap_buffers ();
+  free (data);
 }
 
 static void
@@ -335,35 +257,45 @@ arabic_joining (hb_buffer_t *buffer)
 }
 
 static void
-preprocess_text_arabic (const hb_ot_shape_plan_t *plan,
-			hb_buffer_t              *buffer,
-			hb_font_t                *font)
+setup_masks_arabic (const hb_ot_shape_plan_t *plan,
+		    hb_buffer_t              *buffer,
+		    hb_font_t                *font)
 {
   const arabic_shape_plan_t *arabic_plan = (const arabic_shape_plan_t *) plan->data;
 
-  if (unlikely (arabic_plan->do_fallback))
-  {
-    arabic_joining (buffer);
-    arabic_fallback_shape (font, buffer);
-  }
+  arabic_joining (buffer);
+  unsigned int count = buffer->len;
+  for (unsigned int i = 0; i < count; i++)
+    buffer->info[i].mask |= arabic_plan->mask_array[buffer->info[i].arabic_shaping_action()];
 }
 
+
 static void
-setup_masks_arabic (const hb_ot_shape_plan_t *plan,
-		    hb_buffer_t              *buffer,
-		    hb_font_t                *font)
+arabic_fallback_shape (const hb_ot_shape_plan_t *plan,
+		       hb_font_t *font,
+		       hb_buffer_t *buffer)
 {
   const arabic_shape_plan_t *arabic_plan = (const arabic_shape_plan_t *) plan->data;
 
-  if (likely (!arabic_plan->do_fallback))
+  if (!arabic_plan->do_fallback)
+    return;
+
+retry:
+  arabic_fallback_plan_t *fallback_plan = (arabic_fallback_plan_t *) hb_atomic_ptr_get (&arabic_plan->fallback_plan);
+  if (unlikely (!fallback_plan))
   {
-    arabic_joining (buffer);
-    unsigned int count = buffer->len;
-    for (unsigned int i = 0; i < count; i++)
-      buffer->info[i].mask |= arabic_plan->mask_array[buffer->info[i].arabic_shaping_action()];
+    /* This sucks.  We need a font to build the fallback plan... */
+    fallback_plan = arabic_fallback_plan_create (plan, font);
+    if (unlikely (!hb_atomic_ptr_cmpexch (&(const_cast<arabic_shape_plan_t *> (arabic_plan))->fallback_plan, NULL, fallback_plan))) {
+      arabic_fallback_plan_destroy (fallback_plan);
+      goto retry;
+    }
   }
+
+  arabic_fallback_plan_shape (fallback_plan, font, buffer);
 }
 
+
 const hb_ot_complex_shaper_t _hb_ot_complex_shaper_arabic =
 {
   "arabic",
@@ -371,7 +303,7 @@ const hb_ot_complex_shaper_t _hb_ot_complex_shaper_arabic =
   NULL, /* override_features */
   data_create_arabic,
   data_destroy_arabic,
-  preprocess_text_arabic,
+  NULL, /* preprocess_text_arabic */
   NULL, /* normalization_preference */
   setup_masks_arabic,
   true, /* zero_width_attached_marks */
diff --git a/src/hb-ot-shape.cc b/src/hb-ot-shape.cc
index a19c8b2..1dd8b0a 100644
--- a/src/hb-ot-shape.cc
+++ b/src/hb-ot-shape.cc
@@ -360,10 +360,8 @@ hb_synthesize_glyph_classes (hb_ot_shape_context_t *c)
 static inline void
 hb_ot_substitute_default (hb_ot_shape_context_t *c)
 {
-  if (c->plan->shaper->preprocess_text) {
-    hb_synthesize_glyph_classes (c); /* XXX This is a hack for now. */
+  if (c->plan->shaper->preprocess_text)
     c->plan->shaper->preprocess_text (c->plan, c->buffer, c->font);
-  }
 
   hb_ot_mirror_chars (c);
 
@@ -393,8 +391,7 @@ hb_ot_substitute_complex (hb_ot_shape_context_t *c)
   if (!hb_ot_layout_has_glyph_classes (c->face))
     hb_synthesize_glyph_classes (c);
 
-  if (hb_ot_layout_has_substitution (c->face))
-    c->plan->substitute (c->font, c->buffer);
+  c->plan->substitute (c->font, c->buffer);
 
   hb_ot_layout_substitute_finish (c->font, c->buffer);
 
diff --git a/src/hb-private.hh b/src/hb-private.hh
index 8655020..43b6e8a 100644
--- a/src/hb-private.hh
+++ b/src/hb-private.hh
@@ -77,6 +77,8 @@ static inline Type MAX (const Type &a, const Type &b) { return a > b ? a : b; }
 #undef  ARRAY_LENGTH
 template <typename Type, unsigned int n>
 static inline unsigned int ARRAY_LENGTH (const Type (&a)[n]) { return n; }
+/* A const version, but does not detect erratically being called on pointers. */
+#define ARRAY_LENGTH_CONST(__array) ((signed int) (sizeof (__array) / sizeof (__array[0])))
 
 #define HB_STMT_START do
 #define HB_STMT_END   while (0)
@@ -581,12 +583,13 @@ _hb_debug_msg_va (const char *what,
     fprintf (stderr, "   " VRBAR LBAR);
 
   if (func) {
-    /* If there's a class name, just write that. */
-    const char *dotdot = strstr (func, "::");
+    /* Skip return type */
     const char *space = strchr (func, ' ');
-    if (space && dotdot && space < dotdot)
+    if (space)
       func = space + 1;
-    unsigned int func_len = dotdot ? dotdot - func : strlen (func);
+    /* Skip parameter list */
+    const char *paren = strchr (func, '(');
+    unsigned int func_len = paren ? paren - func : strlen (func);
     fprintf (stderr, "%.*s: ", func_len, func);
   }
 



More information about the HarfBuzz mailing list