[justify] Document algorithm

This commit is contained in:
Behdad Esfahbod 2023-03-02 13:42:52 -07:00
parent c98bb4cf9c
commit 43dbdd9db6
1 changed files with 26 additions and 0 deletions

View File

@ -246,6 +246,9 @@ reset_buffer (hb_buffer_t *buffer,
* In addition, justify the shaping results such that the shaping results reach * In addition, justify the shaping results such that the shaping results reach
* the target advance width/height, depending on the buffer direction. * the target advance width/height, depending on the buffer direction.
* *
* If the advance of the buffer shaped with hb_shape_full() is already known,
* put that in *advance. Otherwise set *advance to zero.
*
* This API is currently experimental and will probably change in the future. * This API is currently experimental and will probably change in the future.
* *
* Return value: false if all shapers failed, true otherwise * Return value: false if all shapers failed, true otherwise
@ -266,6 +269,7 @@ hb_shape_justify (hb_font_t *font,
{ {
// TODO Negative font scales? // TODO Negative font scales?
/* If default advance already matches target, nothing to do. Shape and return. */
if (min_target_advance <= *advance && *advance <= max_target_advance) if (min_target_advance <= *advance && *advance <= max_target_advance)
return hb_shape_full (font, buffer, return hb_shape_full (font, buffer,
features, num_features, features, num_features,
@ -273,6 +277,8 @@ hb_shape_justify (hb_font_t *font,
hb_face_t *face = font->face; hb_face_t *face = font->face;
/* Choose variation tag to use for justification. */
hb_tag_t tag = HB_TAG_NONE; hb_tag_t tag = HB_TAG_NONE;
hb_ot_var_axis_info_t axis_info; hb_ot_var_axis_info_t axis_info;
@ -288,6 +294,7 @@ hb_shape_justify (hb_font_t *font,
break; break;
} }
/* If no suitable variation axis found, can't justify. Just shape and return. */
if (!tag) if (!tag)
{ {
if (hb_shape_full (font, buffer, if (hb_shape_full (font, buffer,
@ -301,6 +308,7 @@ hb_shape_justify (hb_font_t *font,
return false; return false;
} }
/* Copy buffer text as we need it so we can shape multiple times. */
unsigned text_len = buffer->len; unsigned text_len = buffer->len;
auto *text_info = (hb_glyph_info_t *) hb_malloc (text_len * sizeof (buffer->info[0])); auto *text_info = (hb_glyph_info_t *) hb_malloc (text_len * sizeof (buffer->info[0]));
if (unlikely (text_len && !text_info)) if (unlikely (text_len && !text_info))
@ -308,6 +316,7 @@ hb_shape_justify (hb_font_t *font,
hb_memcpy (text_info, buffer->info, text_len * sizeof (buffer->info[0])); hb_memcpy (text_info, buffer->info, text_len * sizeof (buffer->info[0]));
auto text = hb_array<const hb_glyph_info_t> (text_info, text_len); auto text = hb_array<const hb_glyph_info_t> (text_info, text_len);
/* If default advance was not provided to us, calculate it. */
if (!*advance) if (!*advance)
{ {
hb_font_set_variation (font, tag, axis_info.default_value); hb_font_set_variation (font, tag, axis_info.default_value);
@ -318,16 +327,23 @@ hb_shape_justify (hb_font_t *font,
*advance = buffer_advance (buffer); *advance = buffer_advance (buffer);
} }
/* If default advance already matches target, nothing to do. Shape and return.
* Do this again, in case advance was just calculated.
*/
if (min_target_advance <= *advance && *advance <= max_target_advance) if (min_target_advance <= *advance && *advance <= max_target_advance)
return true; return true;
/* Prepare for running the solver. */
double a, b, ya, yb; double a, b, ya, yb;
if (*advance < min_target_advance) if (*advance < min_target_advance)
{ {
/* Need to expand. */
ya = (double) *advance; ya = (double) *advance;
a = (double) axis_info.default_value; a = (double) axis_info.default_value;
b = (double) axis_info.max_value; b = (double) axis_info.max_value;
/* Shape buffer for maximum expansion to use as other
* starting point for the solver. */
hb_font_set_variation (font, tag, (float) b); hb_font_set_variation (font, tag, (float) b);
reset_buffer (buffer, text); reset_buffer (buffer, text);
if (!hb_shape_full (font, buffer, if (!hb_shape_full (font, buffer,
@ -335,6 +351,8 @@ hb_shape_justify (hb_font_t *font,
shaper_list)) shaper_list))
return false; return false;
yb = (double) buffer_advance (buffer); yb = (double) buffer_advance (buffer);
/* If the maximum expansion is less than max target,
* there's nothing to solve for. Just return it. */
if (yb <= (double) max_target_advance) if (yb <= (double) max_target_advance)
{ {
*advance = (float) yb; *advance = (float) yb;
@ -343,10 +361,13 @@ hb_shape_justify (hb_font_t *font,
} }
else else
{ {
/* Need to shrink. */
yb = (double) *advance; yb = (double) *advance;
a = (double) axis_info.min_value; a = (double) axis_info.min_value;
b = (double) axis_info.default_value; b = (double) axis_info.default_value;
/* Shape buffer for maximum shrinkate to use as other
* starting point for the solver. */
hb_font_set_variation (font, tag, (float) a); hb_font_set_variation (font, tag, (float) a);
reset_buffer (buffer, text); reset_buffer (buffer, text);
if (!hb_shape_full (font, buffer, if (!hb_shape_full (font, buffer,
@ -354,6 +375,8 @@ hb_shape_justify (hb_font_t *font,
shaper_list)) shaper_list))
return false; return false;
ya = (double) buffer_advance (buffer); ya = (double) buffer_advance (buffer);
/* If the maximum shrinkate is more than min target,
* there's nothing to solve for. Just return it. */
if (ya >= (double) min_target_advance) if (ya >= (double) min_target_advance)
{ {
*advance = (float) ya; *advance = (float) ya;
@ -361,6 +384,9 @@ hb_shape_justify (hb_font_t *font,
} }
} }
/* Run the solver to find a var axis value that hits
* the desired width. */
double epsilon = (b - a) / (1<<14); double epsilon = (b - a) / (1<<14);
bool failed = false; bool failed = false;