mirror of
https://github.com/harfbuzz/harfbuzz.git
synced 2025-04-09 23:09:21 +00:00
[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
This commit is contained in:
parent
6a3ca37373
commit
43236ce345
3 changed files with 150 additions and 98 deletions
|
@ -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
|
||||
{
|
||||
errno = 0;
|
||||
char *pend;
|
||||
float min_val = strtof (s, &pend);
|
||||
if (errno || s == pend || pend != pp)
|
||||
{
|
||||
g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
|
||||
"Failed parsing axis value at: '%s'", s);
|
||||
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'))
|
||||
{
|
||||
g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
|
||||
"Failed parsing axis value at: '%s'", s);
|
||||
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;
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
gboolean drop;
|
||||
float min, def, max;
|
||||
if (!parse_axis_position(s, &min, &def, &max, &drop, error))
|
||||
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;
|
||||
}
|
||||
if (drop)
|
||||
{
|
||||
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;
|
||||
}
|
||||
#ifdef HB_EXPERIMENTAL_API
|
||||
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,
|
||||
"Cannot pin axis: '%c%c%c%c', not present in fvar", HB_UNTAG (axis_tag));
|
||||
return false;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
#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…
Add table
Reference in a new issue