[HarfBuzz] harfbuzz: Branch 'master' - 3 commits

Behdad Esfahbod behdad at kemper.freedesktop.org
Fri Sep 7 11:56:12 PDT 2012


 TODO                                                                       |    5 
 src/hb-buffer-private.hh                                                   |    2 
 src/hb-buffer.cc                                                           |   34 +
 src/hb-buffer.h                                                            |   14 
 src/hb-common.cc                                                           |    9 
 src/hb-common.h                                                            |    6 
 src/hb-ot-shape-complex-indic-machine.rl                                   |    4 
 src/hb-ot-shape-complex-indic.cc                                           |   56 ++-
 src/hb-shape.cc                                                            |  173 ++++++++++
 src/hb-shape.h                                                             |   13 
 test/shaping/texts/in-tree/shaper-indic/indic/script-sinhala/misc/misc.txt |    1 
 util/options.cc                                                            |  132 -------
 12 files changed, 300 insertions(+), 149 deletions(-)

New commits:
commit 166b5cf7ec2d37fb54a909c437ccdec1edaf37aa
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Fri Sep 7 14:55:07 2012 -0400

    [Indic] Find syllables before any features are applied
    
    With FreeSerif, it seems that the 'ccmp' feature does ligature
    substituttions.  That was then causing syllable match failures.  We now
    find syllables before any features have been applied.
    
    Test sequence: U+0D9A,U+0DCA,U+200D,U+0DBB,U+0DCF

diff --git a/src/hb-ot-shape-complex-indic-machine.rl b/src/hb-ot-shape-complex-indic-machine.rl
index 03e3910..b745466 100644
--- a/src/hb-ot-shape-complex-indic-machine.rl
+++ b/src/hb-ot-shape-complex-indic-machine.rl
@@ -82,7 +82,7 @@ main := |*
 	consonant_syllable	=> { found_syllable (consonant_syllable); };
 	vowel_syllable		=> { found_syllable (vowel_syllable); };
 	standalone_cluster	=> { found_syllable (standalone_cluster); };
-	broken_cluster		=> { found_syllable (broken_cluster); *had_broken_cluster = true; };
+	broken_cluster		=> { found_syllable (broken_cluster); };
 	other			=> { found_syllable (non_indic_cluster); };
 *|;
 
@@ -100,7 +100,7 @@ main := |*
   } HB_STMT_END
 
 static void
-find_syllables (const hb_ot_shape_plan_t *plan, hb_buffer_t *buffer, bool *had_broken_cluster)
+find_syllables (hb_buffer_t *buffer)
 {
   unsigned int p, pe, eof, ts, te, act;
   int cs;
diff --git a/src/hb-ot-shape-complex-indic.cc b/src/hb-ot-shape-complex-indic.cc
index 7f6b79a..6a87c1e 100644
--- a/src/hb-ot-shape-complex-indic.cc
+++ b/src/hb-ot-shape-complex-indic.cc
@@ -203,6 +203,10 @@ enum {
 };
 
 static void
+setup_syllables (const hb_ot_shape_plan_t *plan,
+		 hb_font_t *font,
+		 hb_buffer_t *buffer);
+static void
 initial_reordering (const hb_ot_shape_plan_t *plan,
 		    hb_font_t *font,
 		    hb_buffer_t *buffer);
@@ -216,6 +220,9 @@ collect_features_indic (hb_ot_shape_planner_t *plan)
 {
   hb_ot_map_builder_t *map = &plan->map;
 
+  /* Do this before any lookups have been applied. */
+  map->add_gsub_pause (setup_syllables);
+
   map->add_bool_feature (HB_TAG('l','o','c','l'));
   /* The Indic specs do not require ccmp, but we apply it here since if
    * there is a use of it, it's typically at the beginning. */
@@ -349,6 +356,17 @@ consonant_position_from_face (const indic_shape_plan_t *indic_plan,
 }
 
 
+enum syllable_type_t {
+  consonant_syllable,
+  vowel_syllable,
+  standalone_cluster,
+  broken_cluster,
+  non_indic_cluster,
+};
+
+#include "hb-ot-shape-complex-indic-machine.hh"
+
+
 static void
 setup_masks_indic (const hb_ot_shape_plan_t *plan HB_UNUSED,
 		   hb_buffer_t              *buffer,
@@ -365,6 +383,14 @@ setup_masks_indic (const hb_ot_shape_plan_t *plan HB_UNUSED,
     set_indic_properties (buffer->info[i]);
 }
 
+static void
+setup_syllables (const hb_ot_shape_plan_t *plan HB_UNUSED,
+		 hb_font_t *font HB_UNUSED,
+		 hb_buffer_t *buffer)
+{
+  find_syllables (buffer);
+}
+
 static int
 compare_indic_order (const hb_glyph_info_t *pa, const hb_glyph_info_t *pb)
 {
@@ -788,16 +814,6 @@ initial_reordering_non_indic_cluster (const hb_ot_shape_plan_t *plan HB_UNUSED,
 }
 
 
-enum syllable_type_t {
-  consonant_syllable,
-  vowel_syllable,
-  standalone_cluster,
-  broken_cluster,
-  non_indic_cluster,
-};
-
-#include "hb-ot-shape-complex-indic-machine.hh"
-
 static void
 initial_reordering_syllable (const hb_ot_shape_plan_t *plan,
 			     hb_buffer_t *buffer,
@@ -813,11 +829,23 @@ initial_reordering_syllable (const hb_ot_shape_plan_t *plan,
   }
 }
 
-static void
+static inline void
 insert_dotted_circles (const hb_ot_shape_plan_t *plan,
 		       hb_font_t *font,
 		       hb_buffer_t *buffer)
 {
+  /* Note: This loop is extra overhead, but should not be measurable. */
+  bool has_broken_syllables = false;
+  unsigned int count = buffer->len;
+  for (unsigned int i = 0; i < count; i++)
+    if ((buffer->info[i].syllable() & 0x0F) == broken_cluster) {
+      has_broken_syllables = true;
+      break;
+    }
+  if (likely (!has_broken_syllables))
+    return;
+
+
   hb_codepoint_t dottedcircle_glyph;
   if (!font->get_glyph (0x25CC, 0, &dottedcircle_glyph))
     return;
@@ -856,11 +884,7 @@ initial_reordering (const hb_ot_shape_plan_t *plan,
 		    hb_buffer_t *buffer)
 {
   update_consonant_positions (plan, font, buffer);
-
-  bool had_broken_clusters = false;
-  find_syllables (plan, buffer, &had_broken_clusters);
-  if (unlikely (had_broken_clusters))
-    insert_dotted_circles (plan, font, buffer);
+  insert_dotted_circles (plan, font, buffer);
 
   hb_glyph_info_t *info = buffer->info;
   unsigned int count = buffer->len;
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-sinhala/misc/misc.txt b/test/shaping/texts/in-tree/shaper-indic/indic/script-sinhala/misc/misc.txt
index c8c939a..8715f72 100644
--- a/test/shaping/texts/in-tree/shaper-indic/indic/script-sinhala/misc/misc.txt
+++ b/test/shaping/texts/in-tree/shaper-indic/indic/script-sinhala/misc/misc.txt
@@ -36,3 +36,4 @@
 න්‍ගේ
 න‍්ගේ
 ර්‍
+ක්‍රා
commit 96fdc04e5c6daafce3d45e7508418e4db94df44c
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Thu Sep 6 22:26:16 2012 -0400

    Add hb_buffer_[sg]et_content_type
    
    And hb_buffer_content_type_t and enum values.

diff --git a/TODO b/TODO
index be3c7c6..d94d875 100644
--- a/TODO
+++ b/TODO
@@ -91,3 +91,4 @@ Tests to write:
 - hb_cache_t and relatives
 
 - hb_feature_to/from_string
+- hb_buffer_[sg]et_contents
diff --git a/src/hb-buffer-private.hh b/src/hb-buffer-private.hh
index 456e1b8..f5d64f3 100644
--- a/src/hb-buffer-private.hh
+++ b/src/hb-buffer-private.hh
@@ -92,6 +92,8 @@ struct hb_buffer_t {
 
   /* Buffer contents */
 
+  hb_buffer_content_type_t content_type;
+
   bool in_error; /* Allocation failed */
   bool have_output; /* Whether we have an output buffer going on */
   bool have_positions; /* Whether we have positions */
diff --git a/src/hb-buffer.cc b/src/hb-buffer.cc
index f25a8bc..fec9225 100644
--- a/src/hb-buffer.cc
+++ b/src/hb-buffer.cc
@@ -147,6 +147,7 @@ hb_buffer_t::reset (void)
   hb_segment_properties_t default_props = _HB_BUFFER_PROPS_DEFAULT;
   props = default_props;
 
+  content_type = HB_BUFFER_CONTENT_TYPE_INVALID;
   in_error = false;
   have_output = false;
   have_positions = false;
@@ -446,6 +447,9 @@ hb_buffer_t::merge_out_clusters (unsigned int start,
 void
 hb_buffer_t::guess_properties (void)
 {
+  if (unlikely (!len)) return;
+  assert (content_type == HB_BUFFER_CONTENT_TYPE_UNICODE);
+
   /* If script is set to INVALID, guess from buffer contents */
   if (props.script == HB_SCRIPT_INVALID) {
     for (unsigned int i = 0; i < len; i++) {
@@ -564,6 +568,7 @@ hb_buffer_get_empty (void)
     const_cast<hb_unicode_funcs_t *> (&_hb_unicode_funcs_nil),
     _HB_BUFFER_PROPS_DEFAULT,
 
+    HB_BUFFER_CONTENT_TYPE_INVALID,
     true, /* in_error */
     true, /* have_output */
     true  /* have_positions */
@@ -610,6 +615,20 @@ hb_buffer_get_user_data (hb_buffer_t        *buffer,
 
 
 void
+hb_buffer_set_content_type (hb_buffer_t              *buffer,
+			    hb_buffer_content_type_t  content_type)
+{
+  buffer->content_type = content_type;
+}
+
+hb_buffer_content_type_t
+hb_buffer_get_content_type (hb_buffer_t *buffer)
+{
+  return buffer->content_type;
+}
+
+
+void
 hb_buffer_set_unicode_funcs (hb_buffer_t        *buffer,
 			     hb_unicode_funcs_t *unicode)
 {
@@ -849,6 +868,11 @@ hb_buffer_add_utf8 (hb_buffer_t  *buffer,
 		    unsigned int  item_offset,
 		    int           item_length)
 {
+  assert (buffer->content_type == HB_BUFFER_CONTENT_TYPE_UNICODE ||
+	  (!buffer->len && buffer->content_type == HB_BUFFER_CONTENT_TYPE_INVALID));
+  if (unlikely (hb_object_is_inert (buffer)))
+    return;
+  buffer->content_type = HB_BUFFER_CONTENT_TYPE_UNICODE;
 #define UTF_NEXT(S, E, U)	hb_utf8_next (S, E, &(U))
   ADD_UTF (uint8_t);
 #undef UTF_NEXT
@@ -883,6 +907,11 @@ hb_buffer_add_utf16 (hb_buffer_t    *buffer,
 		     unsigned int    item_offset,
 		     int            item_length)
 {
+  assert (buffer->content_type == HB_BUFFER_CONTENT_TYPE_UNICODE ||
+	  (!buffer->len && buffer->content_type == HB_BUFFER_CONTENT_TYPE_INVALID));
+  if (unlikely (hb_object_is_inert (buffer)))
+    return;
+  buffer->content_type = HB_BUFFER_CONTENT_TYPE_UNICODE;
 #define UTF_NEXT(S, E, U)	hb_utf16_next (S, E, &(U))
   ADD_UTF (uint16_t);
 #undef UTF_NEXT
@@ -895,6 +924,11 @@ hb_buffer_add_utf32 (hb_buffer_t    *buffer,
 		     unsigned int    item_offset,
 		     int             item_length)
 {
+  assert (buffer->content_type == HB_BUFFER_CONTENT_TYPE_UNICODE ||
+	  (!buffer->len && buffer->content_type == HB_BUFFER_CONTENT_TYPE_INVALID));
+  if (unlikely (hb_object_is_inert (buffer)))
+    return;
+  buffer->content_type = HB_BUFFER_CONTENT_TYPE_UNICODE;
 #define UTF_NEXT(S, E, U)	((U) = *(S), (S)+1)
   ADD_UTF (uint32_t);
 #undef UTF_NEXT
diff --git a/src/hb-buffer.h b/src/hb-buffer.h
index aebf482..d89dce3 100644
--- a/src/hb-buffer.h
+++ b/src/hb-buffer.h
@@ -62,6 +62,12 @@ typedef struct hb_glyph_position_t {
   hb_var_int_t   var;
 } hb_glyph_position_t;
 
+typedef enum {
+  HB_BUFFER_CONTENT_TYPE_INVALID = 0,
+  HB_BUFFER_CONTENT_TYPE_UNICODE,
+  HB_BUFFER_CONTENT_TYPE_GLYPHS
+} hb_buffer_content_type_t;
+
 
 hb_buffer_t *
 hb_buffer_create (void);
@@ -88,6 +94,14 @@ hb_buffer_get_user_data (hb_buffer_t        *buffer,
 
 
 void
+hb_buffer_set_content_type (hb_buffer_t              *buffer,
+			    hb_buffer_content_type_t  content_type);
+
+hb_buffer_content_type_t
+hb_buffer_get_content_type (hb_buffer_t *buffer);
+
+
+void
 hb_buffer_set_unicode_funcs (hb_buffer_t        *buffer,
 			     hb_unicode_funcs_t *unicode_funcs);
 
diff --git a/src/hb-shape.cc b/src/hb-shape.cc
index 6619c19..4d64823 100644
--- a/src/hb-shape.cc
+++ b/src/hb-shape.cc
@@ -253,11 +253,16 @@ hb_shape_full (hb_font_t          *font,
   if (unlikely (!buffer->len))
     return true;
 
+  assert (buffer->content_type == HB_BUFFER_CONTENT_TYPE_UNICODE);
+
   buffer->guess_properties ();
 
   hb_shape_plan_t *shape_plan = hb_shape_plan_create_cached (font->face, &buffer->props, features, num_features, shaper_list);
   hb_bool_t res = hb_shape_plan_execute (shape_plan, font, buffer, features, num_features);
   hb_shape_plan_destroy (shape_plan);
+
+  if (res)
+    buffer->content_type = HB_BUFFER_CONTENT_TYPE_GLYPHS;
   return res;
 }
 
commit e30ebd2794b37bd8ec716a258d5cb017fb1dfadc
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Thu Sep 6 22:09:06 2012 -0400

    Add hb_feature_to/from_string()

diff --git a/TODO b/TODO
index 1ca2480..be3c7c6 100644
--- a/TODO
+++ b/TODO
@@ -41,8 +41,6 @@ API additions
 
 - Buffer (de)serialize API ala hb-shape?
 
-- Move feature parsing from util into the library
-
 - Add hb-cairo glue
 
 - Add sanitize API (and a cached version, that saves result on blob user-data)
@@ -91,3 +89,5 @@ Tests to write:
 - hb_set_t
 
 - hb_cache_t and relatives
+
+- hb_feature_to/from_string
diff --git a/src/hb-common.cc b/src/hb-common.cc
index 1301ab2..33a514d 100644
--- a/src/hb-common.cc
+++ b/src/hb-common.cc
@@ -58,6 +58,15 @@ hb_tag_from_string (const char *s, int len)
   return HB_TAG_CHAR4 (tag);
 }
 
+void
+hb_tag_to_string (hb_tag_t tag, char *buf)
+{
+  buf[0] = (char) (uint8_t) (tag >> 24);
+  buf[1] = (char) (uint8_t) (tag >> 16);
+  buf[2] = (char) (uint8_t) (tag >>  8);
+  buf[3] = (char) (uint8_t) (tag >>  0);
+}
+
 
 /* hb_direction_t */
 
diff --git a/src/hb-common.h b/src/hb-common.h
index 920bd32..cc221d3 100644
--- a/src/hb-common.h
+++ b/src/hb-common.h
@@ -95,10 +95,14 @@ typedef uint32_t hb_tag_t;
 
 #define HB_TAG_NONE HB_TAG(0,0,0,0)
 
-/* len=-1 means str is NUL-terminated */
+/* len=-1 means str is NUL-terminated. */
 hb_tag_t
 hb_tag_from_string (const char *str, int len);
 
+/* buf should have 4 bytes. */
+void
+hb_tag_to_string (hb_tag_t tag, char *buf);
+
 
 /* hb_direction_t */
 
diff --git a/src/hb-shape.cc b/src/hb-shape.cc
index 5aa587b..6619c19 100644
--- a/src/hb-shape.cc
+++ b/src/hb-shape.cc
@@ -1,5 +1,6 @@
 /*
  * Copyright © 2009  Red Hat, Inc.
+ * Copyright © 2012  Google, Inc.
  *
  *  This is part of HarfBuzz, a text shaping library.
  *
@@ -22,6 +23,7 @@
  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
  *
  * Red Hat Author(s): Behdad Esfahbod
+ * Google Author(s): Behdad Esfahbod
  */
 
 #include "hb-private.hh"
@@ -32,6 +34,172 @@
 #include "hb-font-private.hh"
 
 
+static void
+parse_space (const char **pp, const char *end)
+{
+  char c;
+#define ISSPACE(c) ((c)==' '||(c)=='\f'||(c)=='\n'||(c)=='\r'||(c)=='\t'||(c)=='\v')
+  while (*pp < end && (c = **pp, ISSPACE (c)))
+    (*pp)++;
+#undef ISSPACE
+}
+
+static hb_bool_t
+parse_char (const char **pp, const char *end, char c)
+{
+  parse_space (pp, end);
+
+  if (*pp == end || **pp != c)
+    return false;
+
+  (*pp)++;
+  return true;
+}
+
+static hb_bool_t
+parse_uint (const char **pp, const char *end, unsigned int *pv)
+{
+  char buf[32];
+  strncpy (buf, *pp, end - *pp);
+  buf[ARRAY_LENGTH (buf) - 1] = '\0';
+
+  char *p = buf;
+  char *pend = p;
+  unsigned int v;
+
+  v = strtol (p, &pend, 0);
+
+  if (p == pend)
+    return false;
+
+  *pv = v;
+  *pp += pend - p;
+  return true;
+}
+
+static hb_bool_t
+parse_feature_value_prefix (const char **pp, const char *end, hb_feature_t *feature)
+{
+  if (parse_char (pp, end, '-'))
+    feature->value = 0;
+  else {
+    parse_char (pp, end, '+');
+    feature->value = 1;
+  }
+
+  return true;
+}
+
+static hb_bool_t
+parse_feature_tag (const char **pp, const char *end, hb_feature_t *feature)
+{
+  const char *p = *pp;
+  char c;
+
+  parse_space (pp, end);
+
+#define ISALNUM(c) (('a' <= (c) && (c) <= 'z') || ('A' <= (c) && (c) <= 'Z') || ('0' <= (c) && (c) <= '9'))
+  while (*pp < end && (c = **pp, ISALNUM(c)))
+    (*pp)++;
+#undef ISALNUM
+
+  if (p == *pp)
+    return false;
+
+  feature->tag = hb_tag_from_string (p, *pp - p);
+  return true;
+}
+
+static hb_bool_t
+parse_feature_indices (const char **pp, const char *end, hb_feature_t *feature)
+{
+  parse_space (pp, end);
+
+  hb_bool_t has_start;
+
+  feature->start = 0;
+  feature->end = (unsigned int) -1;
+
+  if (!parse_char (pp, end, '['))
+    return true;
+
+  has_start = parse_uint (pp, end, &feature->start);
+
+  if (parse_char (pp, end, ':')) {
+    parse_uint (pp, end, &feature->end);
+  } else {
+    if (has_start)
+      feature->end = feature->start + 1;
+  }
+
+  return parse_char (pp, end, ']');
+}
+
+static hb_bool_t
+parse_feature_value_postfix (const char **pp, const char *end, hb_feature_t *feature)
+{
+  return !parse_char (pp, end, '=') || parse_uint (pp, end, &feature->value);
+}
+
+
+static hb_bool_t
+parse_one_feature (const char **pp, const char *end, hb_feature_t *feature)
+{
+  return parse_feature_value_prefix (pp, end, feature) &&
+	 parse_feature_tag (pp, end, feature) &&
+	 parse_feature_indices (pp, end, feature) &&
+	 parse_feature_value_postfix (pp, end, feature) &&
+	 *pp == end;
+}
+
+hb_bool_t
+hb_feature_from_string (const char *str, int len,
+			hb_feature_t *feature)
+{
+  if (len < 0)
+    len = strlen (str);
+
+  return parse_one_feature (&str, str + len, feature);
+}
+
+void
+hb_feature_to_string (hb_feature_t *feature,
+		      char *buf, unsigned int size)
+{
+  if (unlikely (!size)) return;
+
+  char s[128];
+  unsigned int len = 0;
+  if (feature->value == 0)
+    s[len++] = '-';
+  hb_tag_to_string (feature->tag, s + len);
+  len += 4;
+  while (len && s[len - 1] == ' ')
+    len--;
+  if (feature->start != 0 || feature->start != (unsigned int) -1)
+  {
+    s[len++] = '[';
+    if (feature->start)
+      len += snprintf (s + len, ARRAY_LENGTH (s) - len, "%d", feature->start);
+    if (feature->end != feature->start + 1) {
+      s[len++] = ':';
+      if (feature->end != (unsigned int) -1)
+	len += snprintf (s + len, ARRAY_LENGTH (s) - len, "%d", feature->end);
+    }
+    s[len++] = ']';
+  }
+  if (feature->value > 1)
+  {
+    s[len++] = '=';
+    len += snprintf (s + len, ARRAY_LENGTH (s) - len, "%d", feature->value);
+  }
+  assert (len < ARRAY_LENGTH (s));
+  len = MIN (len, size - 1);
+  memcpy (buf, s, len);
+  s[len] = '\0';
+}
+
+
 static const char **static_shaper_list;
 
 static
diff --git a/src/hb-shape.h b/src/hb-shape.h
index 84bf3e7..90a188d 100644
--- a/src/hb-shape.h
+++ b/src/hb-shape.h
@@ -1,5 +1,6 @@
 /*
  * Copyright © 2009  Red Hat, Inc.
+ * Copyright © 2012  Google, Inc.
  *
  *  This is part of HarfBuzz, a text shaping library.
  *
@@ -22,6 +23,7 @@
  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
  *
  * Red Hat Author(s): Behdad Esfahbod
+ * Google Author(s): Behdad Esfahbod
  */
 
 #ifndef HB_H_IN
@@ -45,6 +47,17 @@ typedef struct hb_feature_t {
   unsigned int  end;
 } hb_feature_t;
 
+/* len=-1 means str is NUL-terminated */
+hb_bool_t
+hb_feature_from_string (const char *str, int len,
+			hb_feature_t *feature);
+
+/* something like 128 bytes is more than enough.
+ * nul-terminates. */
+void
+hb_feature_to_string (hb_feature_t *feature,
+		      char *buf, unsigned int size);
+
 
 void
 hb_shape (hb_font_t           *font,
diff --git a/util/options.cc b/util/options.cc
index c05cee6..dc7aeed 100644
--- a/util/options.cc
+++ b/util/options.cc
@@ -196,130 +196,6 @@ list_shapers (const char *name G_GNUC_UNUSED,
 }
 
 
-
-static void
-parse_space (char **pp)
-{
-  char c;
-#define ISSPACE(c) ((c)==' '||(c)=='\f'||(c)=='\n'||(c)=='\r'||(c)=='\t'||(c)=='\v')
-  while (c = **pp, ISSPACE (c))
-    (*pp)++;
-#undef ISSPACE
-}
-
-static hb_bool_t
-parse_char (char **pp, char c)
-{
-  parse_space (pp);
-
-  if (**pp != c)
-    return false;
-
-  (*pp)++;
-  return true;
-}
-
-static hb_bool_t
-parse_uint (char **pp, unsigned int *pv)
-{
-  char *p = *pp;
-  unsigned int v;
-
-  v = strtol (p, pp, 0);
-
-  if (p == *pp)
-    return false;
-
-  *pv = v;
-  return true;
-}
-
-
-static hb_bool_t
-parse_feature_value_prefix (char **pp, hb_feature_t *feature)
-{
-  if (parse_char (pp, '-'))
-    feature->value = 0;
-  else {
-    parse_char (pp, '+');
-    feature->value = 1;
-  }
-
-  return true;
-}
-
-static hb_bool_t
-parse_feature_tag (char **pp, hb_feature_t *feature)
-{
-  char *p = *pp, c;
-
-  parse_space (pp);
-
-#define ISALNUM(c) (('a' <= (c) && (c) <= 'z') || ('A' <= (c) && (c) <= 'Z') || ('0' <= (c) && (c) <= '9'))
-  while (c = **pp, ISALNUM(c))
-    (*pp)++;
-#undef ISALNUM
-
-  if (p == *pp)
-    return false;
-
-  feature->tag = hb_tag_from_string (p, *pp - p);
-  return true;
-}
-
-static hb_bool_t
-parse_feature_indices (char **pp, hb_feature_t *feature)
-{
-  parse_space (pp);
-
-  hb_bool_t has_start;
-
-  feature->start = 0;
-  feature->end = (unsigned int) -1;
-
-  if (!parse_char (pp, '['))
-    return true;
-
-  has_start = parse_uint (pp, &feature->start);
-
-  if (parse_char (pp, ':')) {
-    parse_uint (pp, &feature->end);
-  } else {
-    if (has_start)
-      feature->end = feature->start + 1;
-  }
-
-  return parse_char (pp, ']');
-}
-
-static hb_bool_t
-parse_feature_value_postfix (char **pp, hb_feature_t *feature)
-{
-  return !parse_char (pp, '=') || parse_uint (pp, &feature->value);
-}
-
-
-static hb_bool_t
-parse_one_feature (char **pp, hb_feature_t *feature)
-{
-  return parse_feature_value_prefix (pp, feature) &&
-	 parse_feature_tag (pp, feature) &&
-	 parse_feature_indices (pp, feature) &&
-	 parse_feature_value_postfix (pp, feature) &&
-	 (parse_char (pp, ',') || **pp == '\0');
-}
-
-static void
-skip_one_feature (char **pp)
-{
-  char *e;
-  e = strchr (*pp, ',');
-  if (e)
-    *pp = e + 1;
-  else
-    *pp = *pp + strlen (*pp);
-}
-
 static gboolean
 parse_features (const char *name G_GNUC_UNUSED,
 	        const char *arg,
@@ -351,11 +227,11 @@ parse_features (const char *name G_GNUC_UNUSED,
   /* now do the actual parsing */
   p = s;
   shape_opts->num_features = 0;
-  while (*p) {
-    if (parse_one_feature (&p, &shape_opts->features[shape_opts->num_features]))
+  while (p && *p) {
+    char *end = strchr (p, ',');
+    if (hb_feature_from_string (p, end ? end - p : -1, &shape_opts->features[shape_opts->num_features]))
       shape_opts->num_features++;
-    else
-      skip_one_feature (&p);
+    p = end ? end + 1 : NULL;
   }
 
   return true;



More information about the HarfBuzz mailing list