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 <beh...@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 <beh...@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 <beh...@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); _______________________________________________ HarfBuzz mailing list HarfBuzz@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/harfbuzz