[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
* 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.
*
* Return value: false if all shapers failed, true otherwise
@ -266,6 +269,7 @@ hb_shape_justify (hb_font_t *font,
{
// 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)
return hb_shape_full (font, buffer,
features, num_features,
@ -273,6 +277,8 @@ hb_shape_justify (hb_font_t *font,
hb_face_t *face = font->face;
/* Choose variation tag to use for justification. */
hb_tag_t tag = HB_TAG_NONE;
hb_ot_var_axis_info_t axis_info;
@ -288,6 +294,7 @@ hb_shape_justify (hb_font_t *font,
break;
}
/* If no suitable variation axis found, can't justify. Just shape and return. */
if (!tag)
{
if (hb_shape_full (font, buffer,
@ -301,6 +308,7 @@ hb_shape_justify (hb_font_t *font,
return false;
}
/* Copy buffer text as we need it so we can shape multiple times. */
unsigned text_len = buffer->len;
auto *text_info = (hb_glyph_info_t *) hb_malloc (text_len * sizeof (buffer->info[0]));
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]));
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)
{
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);
}
/* 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)
return true;
/* Prepare for running the solver. */
double a, b, ya, yb;
if (*advance < min_target_advance)
{
/* Need to expand. */
ya = (double) *advance;
a = (double) axis_info.default_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);
reset_buffer (buffer, text);
if (!hb_shape_full (font, buffer,
@ -335,6 +351,8 @@ hb_shape_justify (hb_font_t *font,
shaper_list))
return false;
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)
{
*advance = (float) yb;
@ -343,10 +361,13 @@ hb_shape_justify (hb_font_t *font,
}
else
{
/* Need to shrink. */
yb = (double) *advance;
a = (double) axis_info.min_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);
reset_buffer (buffer, text);
if (!hb_shape_full (font, buffer,
@ -354,6 +375,8 @@ hb_shape_justify (hb_font_t *font,
shaper_list))
return false;
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)
{
*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);
bool failed = false;