Add hb_buffer_normalize_glyphs() and hb-shape --normalize-glyphs

This reorders glyphs within the cluster to a nominal order.  This should
have no visible effect on the output, but helps with testing, for
getting the same hb-shape output for visually-equal glyphs for each
cluster.
This commit is contained in:
Behdad Esfahbod 2012-07-17 17:09:29 -04:00
parent 25e302da9a
commit 39b17837b4
5 changed files with 118 additions and 8 deletions

View File

@ -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);
}

View File

@ -193,6 +193,19 @@ hb_buffer_get_glyph_positions (hb_buffer_t *buffer,
unsigned int *length); 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 HB_END_DECLS
#endif /* HB_BUFFER_H */ #endif /* HB_BUFFER_H */

View File

@ -737,8 +737,8 @@ hb_in_range (T u, T lo, T hi)
#define FLAG(x) (1<<(x)) #define FLAG(x) (1<<(x))
template <typename T> inline void template <typename T, typename T2> inline void
hb_bubble_sort (T *array, unsigned int len, int(*compar)(const T *, const T *)) hb_bubble_sort (T *array, unsigned int len, int(*compar)(const T *, const T *), T2 *array2)
{ {
if (unlikely (!len)) if (unlikely (!len))
return; return;
@ -748,11 +748,21 @@ hb_bubble_sort (T *array, unsigned int len, int(*compar)(const T *, const T *))
unsigned int new_k = 0; unsigned int new_k = 0;
for (unsigned int j = 0; j < k; j++) for (unsigned int j = 0; j < k; j++)
if (compar (&array[j], &array[j+1]) > 0) { if (compar (&array[j], &array[j+1]) > 0)
T t; {
t = array[j]; {
array[j] = array[j + 1]; T t;
array[j + 1] = 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; new_k = j;
} }
@ -760,6 +770,11 @@ hb_bubble_sort (T *array, unsigned int len, int(*compar)(const T *, const T *))
} while (k); } while (k);
} }
template <typename T> inline void
hb_bubble_sort (T *array, unsigned int len, int(*compar)(const T *, const T *))
{
hb_bubble_sort (array, len, compar, (int *) NULL);
}

View File

@ -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"}, {"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"}, {"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}, {"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} {NULL}
}; };
parser->add_group (entries, parser->add_group (entries,

View File

@ -148,6 +148,7 @@ struct shape_options_t : option_group_t
num_features = 0; num_features = 0;
shapers = NULL; shapers = NULL;
utf8_clusters = false; utf8_clusters = false;
normalize_glyphs = false;
add_options (parser); 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) 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, void shape_closure (const char *text, int text_len,
@ -208,6 +212,7 @@ struct shape_options_t : option_group_t
unsigned int num_features; unsigned int num_features;
char **shapers; char **shapers;
hb_bool_t utf8_clusters; hb_bool_t utf8_clusters;
hb_bool_t normalize_glyphs;
}; };