[instancer] add the option to leave blanks in the min:def:max syntax.

When parsing axis positions in the --variations flag recognize empty values as meaning the existing value for that part. For example:

:300:500

Specifies min = existing, def = 300, max = 500.

See: https://github.com/fonttools/fonttools/issues/3322
pull/4473/head
Garret Rieger 1 year ago
parent 6a3ca37373
commit 43236ce345
  1. 30
      src/hb-subset-input.cc
  2. 2
      src/hb-subset.h
  3. 198
      util/hb-subset.cc

@ -481,16 +481,13 @@ hb_subset_input_pin_axis_location (hb_subset_input_t *input,
* @input: a #hb_subset_input_t object.
* @face: a #hb_face_t object.
* @axis_tag: Tag of the axis
* @axis_min_value: Minimum value of the axis variation range to set
* @axis_max_value: Maximum value of the axis variation range to set
* @axis_def_value: Default value of the axis variation range to set, in case of
* null, it'll be determined automatically
* @axis_min_value: Minimum value of the axis variation range to set, if NaN the existing min will be used.
* @axis_max_value: Maximum value of the axis variation range to set if NaN the existing max will be used.
* @axis_def_value: Default value of the axis variation range to set, if NaN the existing default will be used.
*
* Restricting the range of variation on an axis in the given subset input object.
* New min/default/max values will be clamped if they're not within the fvar axis range.
* If the new default value is null:
* If the fvar axis default value is within the new range, then new default
* value is the same as original default value.
*
* If the fvar axis default value is not within the new range, the new default
* value will be changed to the new min or max value, whichever is closer to the fvar
* axis default.
@ -509,19 +506,22 @@ hb_subset_input_set_axis_range (hb_subset_input_t *input,
hb_tag_t axis_tag,
float axis_min_value,
float axis_max_value,
float *axis_def_value /* IN, maybe NULL */)
float axis_def_value)
{
if (axis_min_value > axis_max_value)
return false;
hb_ot_var_axis_info_t axis_info;
if (!hb_ot_var_find_axis_info (face, axis_tag, &axis_info))
return false;
float new_min_val = hb_clamp(axis_min_value, axis_info.min_value, axis_info.max_value);
float new_max_val = hb_clamp(axis_max_value, axis_info.min_value, axis_info.max_value);
float new_default_val = axis_def_value ? *axis_def_value : axis_info.default_value;
new_default_val = hb_clamp(new_default_val, new_min_val, new_max_val);
float min = !std::isnan(axis_min_value) ? axis_min_value : axis_info.min_value;
float max = !std::isnan(axis_max_value) ? axis_max_value : axis_info.max_value;
float def = !std::isnan(axis_def_value) ? axis_def_value : axis_info.default_value;
if (min > max)
return false;
float new_min_val = hb_clamp(min, axis_info.min_value, axis_info.max_value);
float new_max_val = hb_clamp(max, axis_info.min_value, axis_info.max_value);
float new_default_val = hb_clamp(def, new_min_val, new_max_val);
return input->axes_location.set (axis_tag, Triple (new_min_val, new_default_val, new_max_val));
}
#endif

@ -188,7 +188,7 @@ hb_subset_input_set_axis_range (hb_subset_input_t *input,
hb_tag_t axis_tag,
float axis_min_value,
float axis_max_value,
float *axis_def_value);
float axis_def_value);
HB_EXTERN hb_bool_t
hb_subset_input_override_name_table (hb_subset_input_t *input,

@ -27,9 +27,11 @@
#include "batch.hh"
#include "face-options.hh"
#include "glib.h"
#include "main-font-text.hh"
#include "output-options.hh"
#include <cmath>
#include <hb-subset.h>
static hb_face_t* preprocess_face(hb_face_t* face)
@ -674,6 +676,93 @@ parse_drop_tables (const char *name,
}
#ifndef HB_NO_VAR
// Parses an axis position string and sets min, default, and max to
// the requested values. If a value should be set to it's default value
// then it will be set to NaN.
static gboolean
parse_axis_position(const char* s,
float* min,
float* def,
float* max,
gboolean* drop,
GError **error)
{
const char* part = strpbrk(s, ":");
*drop = false;
if (!part) {
// Single value.
if (strcmp (s, "drop") == 0)
{
*min = NAN;
*def = NAN;
*max = NAN;
*drop = true;
return true;
}
errno = 0;
char *p;
float axis_value = strtof (s, &p);
if (errno || s == p)
{
g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
"Failed parsing axis value at: '%s'", s);
return false;
}
*min = axis_value;
*def = axis_value;
*max = axis_value;
return true;
}
float values[3];
int count = 0;
for (int i = 0; i < 3; i++) {
errno = 0;
count++;
if (!*s || part == s) {
values[i] = NAN;
if (part == nullptr) break;
s = part + 1;
part = strpbrk(s, ":");
continue;
}
char *pend;
values[i] = strtof (s, &pend);
if (errno || s == pend || (part && pend != part))
{
g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
"Failed parsing axis value at: '%s'", s);
return false;
}
if (part == nullptr) break;
s = pend + 1;
part = strpbrk(s, ":");
}
if (count == 2) {
*min = values[0];
*def = NAN;
*max = values[1];
return true;
} else if (count == 3) {
*min = values[0];
*def = values[1];
*max = values[2];
return true;
}
g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
"Failed parsing axis value at: '%s'", s);
return false;
}
static gboolean
parse_instance (const char *name,
const char *arg,
@ -686,9 +775,10 @@ parse_instance (const char *name,
return true;
}
char *s = strtok((char *) arg, "=");
while (s)
char* s;
while ((s = strtok((char *) arg, "=")))
{
arg = nullptr;
unsigned len = strlen (s);
if (len > 4) //Axis tags are 4 bytes.
{
@ -707,89 +797,51 @@ parse_instance (const char *name,
return false;
}
#ifdef HB_EXPERIMENTAL_API
char *pp = s;
pp = strpbrk (pp, ":");
if (pp) // partial instancing
gboolean drop;
float min, def, max;
if (!parse_axis_position(s, &min, &def, &max, &drop, error))
return false;
if (drop)
{
errno = 0;
char *pend;
float min_val = strtof (s, &pend);
if (errno || s == pend || pend != pp)
if (!hb_subset_input_pin_axis_to_default (subset_main->input,
subset_main->face,
axis_tag))
{
g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
"Failed parsing axis value at: '%s'", s);
"Cannot pin axis: '%c%c%c%c', not present in fvar", HB_UNTAG (axis_tag));
return false;
}
pp++;
float max_val = strtof (pp, &pend);
/* we need to specify 2 values or 3 values for partial instancing:
* at least new min and max values, new default is optional */
if (errno || pp == pend || (*pend != ':' && *pend != '\0'))
continue;
}
if (min == def && def == max) {
if (!hb_subset_input_pin_axis_location (subset_main->input,
subset_main->face, axis_tag,
def))
{
g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
"Failed parsing axis value at: '%s'", s);
"Cannot pin axis: '%c%c%c%c', not present in fvar", HB_UNTAG (axis_tag));
return false;
}
/* 3 values are specified */
float *def_val_p = nullptr;
float def_val;
if (*pend == ':')
{
def_val = max_val;
def_val_p = &def_val;
pp = pend + 1;
max_val = strtof (pp, &pend);
if (errno || pp == pend || *pend != '\0')
{
g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
"Failed parsing axis value at: '%s'", s);
return false;
}
}
if (!hb_subset_input_set_axis_range (subset_main->input, subset_main->face, axis_tag, min_val, max_val, def_val_p))
{
g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
"Error: axis: '%c%c%c%c', not present in fvar or invalid range with min:%.6f max:%.6f",
HB_UNTAG (axis_tag), (double) min_val, (double) max_val);
return false;
}
continue;
}
else
{
#endif
if (strcmp (s, "drop") == 0)
{
if (!hb_subset_input_pin_axis_to_default (subset_main->input, subset_main->face, axis_tag))
{
g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
"Cannot pin axis: '%c%c%c%c', not present in fvar", HB_UNTAG (axis_tag));
return false;
}
}
else
{
errno = 0;
char *p;
float axis_value = strtof (s, &p);
if (errno || s == p)
{
g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
"Failed parsing axis value at: '%s'", s);
return false;
}
if (!hb_subset_input_pin_axis_location (subset_main->input, subset_main->face, axis_tag, axis_value))
{
g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
"Cannot pin axis: '%c%c%c%c', not present in fvar", HB_UNTAG (axis_tag));
return false;
}
}
#ifdef HB_EXPERIMENTAL_API
if (!hb_subset_input_set_axis_range (subset_main->input,
subset_main->face, axis_tag,
min, max, def))
{
g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
"Cannot pin axis: '%c%c%c%c', not present in fvar", HB_UNTAG (axis_tag));
return false;
}
continue;
#endif
s = strtok(nullptr, "=");
g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
"Partial instancing is not supported.");
return false;
}
return true;
@ -1028,7 +1080,7 @@ subset_main_t::add_options ()
{"variations", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_instance,
"(Partially|Fully) Instantiate a variable font. A location consists of the tag "
"of a variation axis, followed by '=', followed by a number or the literal "
"string 'drop'. For example: --variations=\"wdth=100 wght=200\" or --instance=\"wdth=drop\""
"string 'drop'. For example: --variations=\"wdth=100 wght=200\" or --variations=\"wdth=drop\""
#ifndef HB_EXPERIMENTAL_API
"\n\nNote: currently only full instancing is supported unless this util has been compiled with experimental api enabled."
#endif

Loading…
Cancel
Save