[unsafe-to-break] Towards verifying unsafe-to-break in --verify
authorBehdad Esfahbod <behdad@behdad.org>
Sat, 12 Aug 2017 02:51:06 +0000 (19:51 -0700)
committerBehdad Esfahbod <behdad@behdad.org>
Sat, 12 Aug 2017 03:00:55 +0000 (20:00 -0700)
We break and shape fragments and reconstruct shape result from them.
Remains to compare to original buffer.  Going to add some buffer
comparison API and use here, instead of open-coding.

src/hb-buffer.cc
util/options.hh

index 297956e..4ab5996 100644 (file)
@@ -1716,7 +1716,8 @@ hb_buffer_append (hb_buffer_t *buffer,
                  unsigned int end)
 {
   assert (!buffer->have_output && !source->have_output);
-  assert (buffer->have_positions == source->have_positions);
+  assert (buffer->have_positions == source->have_positions ||
+         !buffer->len || !source->len);
   assert (buffer->content_type == source->content_type ||
          !buffer->len || !source->len);
 
@@ -1729,6 +1730,8 @@ hb_buffer_append (hb_buffer_t *buffer,
 
   if (!buffer->len)
     buffer->content_type = source->content_type;
+  if (!buffer->have_positions && source->have_positions)
+    buffer->clear_positions ();
 
   if (buffer->len + (end - start) < buffer->len) /* Overflows. */
   {
index fedd121..d134f7a 100644 (file)
@@ -36,6 +36,7 @@
 #include <stddef.h>
 #include <string.h>
 #include <stdio.h>
+#include <assert.h>
 #include <math.h>
 #include <locale.h>
 #include <errno.h>
@@ -216,6 +217,15 @@ struct shape_options_t : option_group_t
     hb_buffer_guess_segment_properties (buffer);
   }
 
+  static void copy_buffer_properties (hb_buffer_t *dst, hb_buffer_t *src)
+  {
+    hb_segment_properties_t props;
+    hb_buffer_get_segment_properties (src, &props);
+    hb_buffer_set_segment_properties (dst, &props);
+    hb_buffer_set_flags (dst, hb_buffer_get_flags (src));
+    hb_buffer_set_cluster_level (dst, hb_buffer_get_cluster_level (src));
+  }
+
   void populate_buffer (hb_buffer_t *buffer, const char *text, int text_len,
                        const char *text_before, const char *text_after)
   {
@@ -246,6 +256,13 @@ struct shape_options_t : option_group_t
 
   hb_bool_t shape (hb_font_t *font, hb_buffer_t *buffer, const char **error=NULL)
   {
+    hb_buffer_t *text_buffer = NULL;
+    if (verify)
+    {
+      text_buffer = hb_buffer_create ();
+      hb_buffer_append (text_buffer, buffer, 0, -1);
+    }
+
     if (!hb_shape_full (font, buffer, features, num_features, shapers))
     {
       if (error)
@@ -256,13 +273,28 @@ struct shape_options_t : option_group_t
     if (normalize_glyphs)
       hb_buffer_normalize_glyphs (buffer);
 
-    if (verify && !verify_buffer (buffer, error))
+    if (verify && !verify_buffer (buffer, text_buffer, font, error))
       return false;
 
+    if (text_buffer)
+      hb_buffer_destroy (text_buffer);
+
     return true;
   }
 
-  bool verify_buffer (hb_buffer_t *buffer, const char **error=NULL)
+  bool verify_buffer (hb_buffer_t  *buffer,
+                     hb_buffer_t  *text_buffer,
+                     hb_font_t    *font,
+                     const char  **error=NULL)
+  {
+    if (!verify_buffer_monotone (buffer, error))
+      return false;
+    if (!verify_buffer_safe_to_break (buffer, text_buffer, font, error))
+      return false;
+    return true;
+  }
+
+  bool verify_buffer_monotone (hb_buffer_t *buffer, const char **error=NULL)
   {
     /* Check that clusters are monotone. */
     if (cluster_level == HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES ||
@@ -286,6 +318,101 @@ struct shape_options_t : option_group_t
     return true;
   }
 
+  bool verify_buffer_safe_to_break (hb_buffer_t  *buffer,
+                                   hb_buffer_t  *text_buffer,
+                                   hb_font_t    *font,
+                                   const char  **error=NULL)
+  {
+    if (cluster_level != HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES &&
+       cluster_level != HB_BUFFER_CLUSTER_LEVEL_MONOTONE_CHARACTERS)
+    {
+      /* Cannot perform this check without monotone clusters.
+       * Then again, unsafe-to-break flag is much harder to use without
+       * monotone clusters. */
+      return true;
+    }
+
+    /* Check that breaking up shaping at safe-to-break is indeed safe. */
+
+    hb_buffer_t *fragment = hb_buffer_create ();
+    hb_buffer_t *reconstruction = hb_buffer_create ();
+    copy_buffer_properties (reconstruction, buffer);
+
+    unsigned int num_glyphs;
+    hb_glyph_info_t *info = hb_buffer_get_glyph_infos (buffer, &num_glyphs);
+
+    unsigned int num_chars;
+    hb_glyph_info_t *text = hb_buffer_get_glyph_infos (text_buffer, &num_chars);
+
+    /* Chop text and shape fragments. */
+    bool forward = HB_DIRECTION_IS_FORWARD (hb_buffer_get_direction (buffer));
+    unsigned int start = 0;
+    unsigned int text_start = forward ? 0 : num_chars;
+    unsigned int text_end = text_start;
+    for (unsigned int end = 1; end < num_glyphs + 1; end++)
+    {
+      if (end < num_glyphs &&
+         (info[end].cluster == info[end-1].cluster ||
+          info[end-(forward?0:1)].mask & HB_GLYPH_FLAG_UNSAFE_TO_BREAK))
+         continue;
+
+      /* Shape segment corresponding to glyphs start..end. */
+      if (end == num_glyphs)
+      {
+        if (forward)
+         text_end = num_chars;
+       else
+         text_start = 0;
+      }
+      else
+      {
+       unsigned int cluster = info[end].cluster;
+       if (forward)
+         while (text_end < num_chars && text[text_end].cluster != cluster)
+           text_end++;
+       else
+         while (text_start && text[text_start - 1].cluster != cluster)
+           text_start--;
+      }
+      assert (text_start < text_end);
+
+      //printf("start %d end %d text start %d end %d\n", start, end, text_start, text_end);
+      hb_buffer_clear_contents (fragment);
+      copy_buffer_properties (fragment, buffer);
+      hb_buffer_append (fragment, text_buffer, text_start, text_end);
+      if (!hb_shape_full (font, fragment, features, num_features, shapers))
+      {
+       if (error)
+         *error = "all shapers failed while shaping fragment.";
+       return false;
+      }
+      hb_buffer_append (reconstruction, fragment, 0, -1);
+
+      start = end;
+      if (forward)
+       text_start = text_end;
+      else
+       text_end = text_start;
+    }
+
+    hb_glyph_position_t *pos = hb_buffer_get_glyph_positions (buffer, NULL);
+
+    unsigned int r_num_glyphs;
+    hb_glyph_info_t *r_info = hb_buffer_get_glyph_infos (reconstruction, &r_num_glyphs);
+    hb_glyph_position_t *r_pos = hb_buffer_get_glyph_positions (reconstruction, NULL);
+
+    /* TODO Compare buffers. Remove assert. */
+    assert (num_glyphs == r_num_glyphs);
+
+    //hb_buffer_set_length (buffer, 0);
+    //hb_buffer_append (buffer, reconstruction, 0, -1);
+
+    hb_buffer_destroy (reconstruction);
+    hb_buffer_destroy (fragment);
+
+    return true;
+  }
+
   void shape_closure (const char *text, int text_len,
                      hb_font_t *font, hb_buffer_t *buffer,
                      hb_set_t *glyphs)