diff --git a/src/hb-subset-input.cc b/src/hb-subset-input.cc index 1e0a89a63..a30266bfc 100644 --- a/src/hb-subset-input.cc +++ b/src/hb-subset-input.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 diff --git a/src/hb-subset.h b/src/hb-subset.h index d79e7f762..9ec6d669a 100644 --- a/src/hb-subset.h +++ b/src/hb-subset.h @@ -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, diff --git a/util/hb-subset.cc b/util/hb-subset.cc index 156831ed1..da03be149 100644 --- a/util/hb-subset.cc +++ b/util/hb-subset.cc @@ -27,9 +27,11 @@ #include "batch.hh" #include "face-options.hh" +#include "glib.h" #include "main-font-text.hh" #include "output-options.hh" +#include #include 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