From 05fabbd03eae7b84ebbce7abbdc55c1d67ceacf9 Mon Sep 17 00:00:00 2001 From: Behdad Esfahbod Date: Fri, 11 Aug 2017 19:51:06 -0700 Subject: [PATCH] [unsafe-to-break] Towards verifying unsafe-to-break in --verify 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 | 5 +- util/options.hh | 131 ++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 133 insertions(+), 3 deletions(-) diff --git a/src/hb-buffer.cc b/src/hb-buffer.cc index 297956e6b..4ab5996fa 100644 --- a/src/hb-buffer.cc +++ b/src/hb-buffer.cc @@ -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. */ { diff --git a/util/options.hh b/util/options.hh index fedd12172..d134f7ac4 100644 --- a/util/options.hh +++ b/util/options.hh @@ -36,6 +36,7 @@ #include #include #include +#include #include #include #include @@ -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)