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

Behdad Esfahbod behdad at kemper.freedesktop.org
Fri Jun 19 13:32:14 PDT 2015


 src/hb-buffer-private.hh |   20 ++++++++++++++
 src/hb-buffer.cc         |   36 ++++++++++++++++++++++++--
 src/hb-ot-shape.cc       |   65 ++++++++++++++++++++++++++++-------------------
 test/api/test-shape.c    |   43 +++++++++++++++++++++++++++++++
 4 files changed, 136 insertions(+), 28 deletions(-)

New commits:
commit 5f13bbd9d4b0970851626e2ce3cf4ecb3cfde801
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Fri Jun 19 13:31:49 2015 -0700

    When removing default-ignorables, merge clusters
    
    Fixes test-shape, and:
    https://code.google.com/p/chromium/issues/detail?id=497578

diff --git a/src/hb-buffer-private.hh b/src/hb-buffer-private.hh
index 6a33962..ced748f 100644
--- a/src/hb-buffer-private.hh
+++ b/src/hb-buffer-private.hh
@@ -192,6 +192,8 @@ struct hb_buffer_t {
 				   unsigned int end);
   HB_INTERNAL void merge_out_clusters (unsigned int start,
 				       unsigned int end);
+  /* Merge clusters for deleting current glyph, and skip it. */
+  HB_INTERNAL void delete_glyph (void);
 
   /* Internal methods */
   HB_INTERNAL bool enlarge (unsigned int size);
diff --git a/src/hb-buffer.cc b/src/hb-buffer.cc
index e13ee4a..4f953f0 100644
--- a/src/hb-buffer.cc
+++ b/src/hb-buffer.cc
@@ -529,7 +529,7 @@ hb_buffer_t::merge_clusters (unsigned int start,
 
   /* If we hit the start of buffer, continue in out-buffer. */
   if (idx == start)
-    for (unsigned i = out_len; i && out_info[i - 1].cluster == info[start].cluster; i--)
+    for (unsigned int i = out_len; i && out_info[i - 1].cluster == info[start].cluster; i--)
       out_info[i - 1].cluster = cluster;
 
   for (unsigned int i = start; i < end; i++)
@@ -561,12 +561,44 @@ hb_buffer_t::merge_out_clusters (unsigned int start,
 
   /* If we hit the end of out-buffer, continue in buffer. */
   if (end == out_len)
-    for (unsigned i = idx; i < len && info[i].cluster == out_info[end - 1].cluster; i++)
+    for (unsigned int i = idx; i < len && info[i].cluster == out_info[end - 1].cluster; i++)
       info[i].cluster = cluster;
 
   for (unsigned int i = start; i < end; i++)
     out_info[i].cluster = cluster;
 }
+void
+hb_buffer_t::delete_glyph ()
+{
+  unsigned int cluster = info[idx].cluster;
+  if (idx + 1 < len && cluster == info[idx + 1].cluster)
+  {
+    /* Cluster survives; do nothing. */
+    goto done;
+  }
+
+  if (out_len)
+  {
+    /* Merge cluster backward. */
+    if (cluster < out_info[out_len - 1].cluster)
+    {
+      unsigned int old_cluster = out_info[out_len - 1].cluster;
+      for (unsigned i = out_len; i && out_info[i - 1].cluster == old_cluster; i--)
+	out_info[i - 1].cluster = cluster;
+    }
+    goto done;
+  }
+
+  if (idx + 1 < len)
+  {
+    /* Merge cluster forward. */
+    merge_clusters (idx, idx + 2);
+    goto done;
+  }
+
+done:
+  skip_glyph ();
+}
 
 void
 hb_buffer_t::guess_segment_properties (void)
diff --git a/src/hb-ot-shape.cc b/src/hb-ot-shape.cc
index 5688604..a531d77 100644
--- a/src/hb-ot-shape.cc
+++ b/src/hb-ot-shape.cc
@@ -692,7 +692,7 @@ hb_ot_hide_default_ignorables (hb_ot_shape_context_t *c)
       if (!_hb_glyph_info_ligated (&info[buffer->idx]) &&
 	   _hb_glyph_info_is_default_ignorable (&info[buffer->idx]))
       {
-        buffer->skip_glyph ();
+	buffer->delete_glyph ();
 	continue;
       }
       buffer->next_glyph ();
commit 82b521aeb7cc73879b44ca4278d6fa8b4347527f
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Fri Jun 19 11:57:57 2015 -0700

    Rewrite hide_default_ignorables
    
    Separate the loops for the two cases of replacing with space
    and deleting.  For deleting, use the out-buffer machinery.
    
    Needed for upcoming cluster merge fix.

diff --git a/src/hb-buffer-private.hh b/src/hb-buffer-private.hh
index 069f925..6a33962 100644
--- a/src/hb-buffer-private.hh
+++ b/src/hb-buffer-private.hh
@@ -151,6 +151,24 @@ struct hb_buffer_t {
 
     idx++;
   }
+  inline void
+  next_glyphs (unsigned int count)
+  {
+    if (have_output)
+    {
+      if (unlikely (out_info != info || out_len != idx)) {
+	if (unlikely (!make_room_for (count, count))) return;
+	{
+	  while (count--)
+	    out_info[out_len++] = info[idx++];
+	  return;
+	}
+      }
+      out_len += count;
+    }
+
+    idx += count;
+  }
 
   /* Advance idx without copying to output. */
   inline void skip_glyph (void) { idx++; }
diff --git a/src/hb-ot-shape.cc b/src/hb-ot-shape.cc
index 5fb0e05..5688604 100644
--- a/src/hb-ot-shape.cc
+++ b/src/hb-ot-shape.cc
@@ -647,45 +647,58 @@ hb_ot_position (hb_ot_shape_context_t *c)
 static void
 hb_ot_hide_default_ignorables (hb_ot_shape_context_t *c)
 {
-  if (c->buffer->flags & HB_BUFFER_FLAG_PRESERVE_DEFAULT_IGNORABLES)
-    return;
+  hb_buffer_t *buffer = c->buffer;
 
-  hb_codepoint_t space;
-  enum {
-    SPACE_DONT_KNOW,
-    SPACE_AVAILABLE,
-    SPACE_UNAVAILABLE
-  } space_status = SPACE_DONT_KNOW;
+  if (buffer->flags & HB_BUFFER_FLAG_PRESERVE_DEFAULT_IGNORABLES)
+    return;
 
-  unsigned int count = c->buffer->len;
-  hb_glyph_info_t *info = c->buffer->info;
-  hb_glyph_position_t *pos = c->buffer->pos;
-  unsigned int j = 0;
-  for (unsigned int i = 0; i < count; i++)
+  unsigned int count = buffer->len;
+  hb_glyph_info_t *info = buffer->info;
+  hb_glyph_position_t *pos = buffer->pos;
+  unsigned int i = 0;
+  for (i = 0; i < count; i++)
   {
     if (unlikely (!_hb_glyph_info_ligated (&info[i]) &&
 		  _hb_glyph_info_is_default_ignorable (&info[i])))
-    {
-      if (space_status == SPACE_DONT_KNOW)
-	space_status = c->font->get_glyph (' ', 0, &space) ? SPACE_AVAILABLE : SPACE_UNAVAILABLE;
+      break;
+  }
+
+  /* No default-ignorables found; return. */
+  if (i == count)
+    return;
 
-      if (space_status == SPACE_AVAILABLE)
+  hb_codepoint_t space;
+  if (c->font->get_glyph (' ', 0, &space))
+  {
+    /* Replace default-ignorables with a zero-advance space glyph. */
+    for (/*continue*/; i < count; i++)
+    {
+      if (!_hb_glyph_info_ligated (&info[i]) &&
+	   _hb_glyph_info_is_default_ignorable (&info[i]))
       {
 	info[i].codepoint = space;
-	pos[i].x_advance = 0;
-	pos[i].y_advance = 0;
+	pos[i].x_advance = pos[i].y_advance = pos[i].x_offset = pos[i].y_offset = 0;
       }
-      else
-	continue; /* Delete it. XXX Merge clusters? */
     }
-    if (j != i)
+  }
+  else
+  {
+    /* Merge clusters and delete default-ignorables. */
+    buffer->clear_output ();
+    buffer->idx = 0;
+    buffer->next_glyphs (i);
+    while (buffer->idx < buffer->len)
     {
-      info[j] = info[i];
-      pos[j] = pos[i];
+      if (!_hb_glyph_info_ligated (&info[buffer->idx]) &&
+	   _hb_glyph_info_is_default_ignorable (&info[buffer->idx]))
+      {
+        buffer->skip_glyph ();
+	continue;
+      }
+      buffer->next_glyph ();
     }
-    j++;
+    buffer->swap_buffers ();
   }
-  c->buffer->len = j;
 }
 
 
commit b3a2f6afbac1956b65f29a17b9dc896e86135329
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Thu Jun 18 17:15:33 2015 -0700

    [test] Add test for cluster merging
    
    Based on test from https://code.google.com/p/chromium/issues/detail?id=497578
    
    Currently fails.  Basically, if there's a default_ignorable at the
    start of text, and font has no space glyph, we remove the default_ignorable,
    and that makes the first char in text to correspond to no cluster.
    
    Fix coming.

diff --git a/test/api/test-shape.c b/test/api/test-shape.c
index ccf6eed..eb24407 100644
--- a/test/api/test-shape.c
+++ b/test/api/test-shape.c
@@ -139,6 +139,48 @@ test_shape (void)
 }
 
 static void
+test_shape_clusters (void)
+{
+  hb_face_t *face;
+  hb_font_t *font;
+  hb_buffer_t *buffer;
+  unsigned int len;
+  hb_glyph_info_t *glyphs;
+
+  face = hb_face_create (NULL, 0);
+  font = hb_font_create (face);
+  hb_face_destroy (face);
+
+  buffer =  hb_buffer_create ();
+  hb_buffer_set_direction (buffer, HB_DIRECTION_LTR);
+  {
+    /* https://code.google.com/p/chromium/issues/detail?id=497578 */
+    hb_codepoint_t test[] = {0xFFF1, 0xF0B6};
+    hb_buffer_add_utf32 (buffer, test, 2, 0, 2);
+  }
+
+  hb_shape (font, buffer, NULL, 0);
+
+  len = hb_buffer_get_length (buffer);
+  glyphs = hb_buffer_get_glyph_infos (buffer, NULL);
+
+  {
+    const hb_codepoint_t output_glyphs[] = {0};
+    const hb_position_t output_clusters[] = {0};
+    unsigned int i;
+    g_assert_cmpint (len, ==, 1);
+    for (i = 0; i < len; i++) {
+      g_assert_cmphex (glyphs[i].codepoint, ==, output_glyphs[i]);
+      g_assert_cmphex (glyphs[i].cluster,   ==, output_clusters[i]);
+    }
+  }
+
+  hb_buffer_destroy (buffer);
+  hb_font_destroy (font);
+}
+
+
+static void
 test_shape_list (void)
 {
   const char **shapers = hb_shape_list_shapers ();
@@ -157,6 +199,7 @@ main (int argc, char **argv)
   hb_test_init (&argc, &argv);
 
   hb_test_add (test_shape);
+  hb_test_add (test_shape_clusters);
   /* TODO test fallback shaper */
   /* TODO test shaper_full */
   hb_test_add (test_shape_list);


More information about the HarfBuzz mailing list