diff --git a/src/hb-buffer.cc b/src/hb-buffer.cc index 44571da0d..ec2965940 100644 --- a/src/hb-buffer.cc +++ b/src/hb-buffer.cc @@ -887,3 +887,79 @@ hb_buffer_add_utf32 (hb_buffer_t *buffer, } +static int +compare_info_codepoint (const hb_glyph_info_t *pa, + const hb_glyph_info_t *pb) +{ + return (int) pb->codepoint - (int) pa->codepoint; +} + +static inline void +normalize_glyphs_cluster (hb_buffer_t *buffer, + unsigned int start, + unsigned int end, + bool backward) +{ + hb_glyph_position_t *pos = buffer->pos; + + /* Total cluster advance */ + hb_position_t total_x_advance = 0, total_y_advance = 0; + for (unsigned int i = start; i < end; i++) + { + total_x_advance += pos[i].x_advance; + total_y_advance += pos[i].y_advance; + } + + hb_position_t x_advance = 0, y_advance = 0; + for (unsigned int i = start; i < end; i++) + { + pos[i].x_offset += x_advance; + pos[i].y_offset += y_advance; + + x_advance += pos[i].x_advance; + y_advance += pos[i].y_advance; + + pos[i].x_advance = 0; + pos[i].y_advance = 0; + } + + if (backward) + { + /* Transfer all cluster advance to the last glyph. */ + pos[end - 1].x_advance = total_x_advance; + pos[end - 1].y_advance = total_y_advance; + + hb_bubble_sort (buffer->info + start, end - start - 1, compare_info_codepoint, buffer->pos + start); + } else { + /* Transfer all cluster advance to the first glyph. */ + pos[start].x_advance += total_x_advance; + pos[start].y_advance += total_y_advance; + for (unsigned int i = start + 1; i < end; i++) { + pos[i].x_offset -= total_x_advance; + pos[i].y_offset -= total_y_advance; + } + hb_bubble_sort (buffer->info + start + 1, end - start - 1, compare_info_codepoint, buffer->pos + start + 1); + } +} + +void +hb_buffer_normalize_glyphs (hb_buffer_t *buffer) +{ + assert (buffer->have_positions); + /* XXX assert (buffer->have_glyphs); */ + + bool backward = HB_DIRECTION_IS_BACKWARD (buffer->props.direction); + + unsigned int count = buffer->len; + if (unlikely (!count)) return; + hb_glyph_info_t *info = buffer->info; + + unsigned int start = 0; + unsigned int end; + for (end = start + 1; end < count; end++) + if (info[start].cluster != info[end].cluster) { + normalize_glyphs_cluster (buffer, start, end, backward); + start = end; + } + normalize_glyphs_cluster (buffer, start, end, backward); +} diff --git a/src/hb-buffer.h b/src/hb-buffer.h index 73adc2e79..d67cfd630 100644 --- a/src/hb-buffer.h +++ b/src/hb-buffer.h @@ -193,6 +193,19 @@ hb_buffer_get_glyph_positions (hb_buffer_t *buffer, unsigned int *length); +/* Reorders a glyph buffer to have canonical in-cluster glyph order / position. + * The resulting clusters should behave identical to pre-reordering clusters. + * NOTE: This has nothing to do with Unicode normalization. */ +void +hb_buffer_normalize_glyphs (hb_buffer_t *buffer); + +/* + * NOT IMPLEMENTED +void +hb_buffer_normalize_characters (hb_buffer_t *buffer); +*/ + + HB_END_DECLS #endif /* HB_BUFFER_H */ diff --git a/src/hb-private.hh b/src/hb-private.hh index 29cd68c24..02ed9ceac 100644 --- a/src/hb-private.hh +++ b/src/hb-private.hh @@ -737,8 +737,8 @@ hb_in_range (T u, T lo, T hi) #define FLAG(x) (1<<(x)) -template inline void -hb_bubble_sort (T *array, unsigned int len, int(*compar)(const T *, const T *)) +template inline void +hb_bubble_sort (T *array, unsigned int len, int(*compar)(const T *, const T *), T2 *array2) { if (unlikely (!len)) return; @@ -748,11 +748,21 @@ hb_bubble_sort (T *array, unsigned int len, int(*compar)(const T *, const T *)) unsigned int new_k = 0; for (unsigned int j = 0; j < k; j++) - if (compar (&array[j], &array[j+1]) > 0) { - T t; - t = array[j]; - array[j] = array[j + 1]; - array[j + 1] = t; + if (compar (&array[j], &array[j+1]) > 0) + { + { + T t; + t = array[j]; + array[j] = array[j + 1]; + array[j + 1] = t; + } + if (array2) + { + T2 t; + t = array2[j]; + array2[j] = array2[j + 1]; + array2[j + 1] = t; + } new_k = j; } @@ -760,6 +770,11 @@ hb_bubble_sort (T *array, unsigned int len, int(*compar)(const T *, const T *)) } while (k); } +template inline void +hb_bubble_sort (T *array, unsigned int len, int(*compar)(const T *, const T *)) +{ + hb_bubble_sort (array, len, compar, (int *) NULL); +} diff --git a/util/options.cc b/util/options.cc index db1b24475..584190e04 100644 --- a/util/options.cc +++ b/util/options.cc @@ -396,6 +396,7 @@ shape_options_t::add_options (option_parser_t *parser) {"language", 0, 0, G_OPTION_ARG_STRING, &this->language, "Set text language (default: $LANG)", "langstr"}, {"script", 0, 0, G_OPTION_ARG_STRING, &this->script, "Set text script (default: auto)", "ISO-15924 tag"}, {"utf8-clusters", 0, 0, G_OPTION_ARG_NONE, &this->utf8_clusters, "Use UTF8 byte indices, not char indices", NULL}, + {"normalize-glyphs",0, 0, G_OPTION_ARG_NONE, &this->normalize_glyphs, "Rearrange glyph clusters in nominal order", NULL}, {NULL} }; parser->add_group (entries, diff --git a/util/options.hh b/util/options.hh index 9b7baa716..24852305b 100644 --- a/util/options.hh +++ b/util/options.hh @@ -148,6 +148,7 @@ struct shape_options_t : option_group_t num_features = 0; shapers = NULL; utf8_clusters = false; + normalize_glyphs = false; add_options (parser); } @@ -188,7 +189,10 @@ struct shape_options_t : option_group_t hb_bool_t shape (hb_font_t *font, hb_buffer_t *buffer) { - return hb_shape_full (font, buffer, features, num_features, shapers); + hb_bool_t res = hb_shape_full (font, buffer, features, num_features, shapers); + if (normalize_glyphs) + hb_buffer_normalize_glyphs (buffer); + return res; } void shape_closure (const char *text, int text_len, @@ -208,6 +212,7 @@ struct shape_options_t : option_group_t unsigned int num_features; char **shapers; hb_bool_t utf8_clusters; + hb_bool_t normalize_glyphs; };