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:
parent
25e302da9a
commit
39b17837b4
@ -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);
|
||||||
|
}
|
||||||
|
@ -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 */
|
||||||
|
@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -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,
|
||||||
|
@ -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;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user