add option "--instance", store axes_location in subset_plan and drop all

variation tables when all axes are pinned at default
This commit is contained in:
Qunxin Liu 2022-06-21 19:29:52 -07:00 committed by Garret Rieger
parent 5744e951fc
commit 2a4773e43d
11 changed files with 267 additions and 30 deletions

View File

@ -19,7 +19,9 @@ symbols = sorted (re.findall (r"^hb_\w+(?= \()", "\n".join (headers_content), re
if '--experimental-api' not in sys.argv:
# Move these to harfbuzz-sections.txt when got stable
experimental_symbols = \
"""hb_subset_repack_or_fail""".splitlines ()
"""hb_subset_repack_or_fail
hb_subset_input_pin_axis_location
hb_subset_input_pin_axis_to_default""".splitlines ()
symbols = [x for x in symbols if x not in experimental_symbols]
symbols = "\n".join (symbols)

View File

@ -455,4 +455,28 @@ struct hb_map_t : hb_hashmap_t<hb_codepoint_t,
hb_map_t (const Iterable &o) : hashmap (o) {}
};
template <typename K, typename V>
static inline
hb_hashmap_t<K, V>* hb_hashmap_create ()
{
using hashmap = hb_hashmap_t<K, V>;
hashmap* map;
if (!(map = hb_object_create<hashmap> ()))
return nullptr;
map->init_shallow ();
return map;
}
template <typename K, typename V>
static inline
void hb_hashmap_destroy (hb_hashmap_t<K, V>* map)
{
if (!hb_object_destroy (map))
return;
map->fini_shallow ();
hb_free (map);
}
#endif /* HB_MAP_HH */

View File

@ -106,6 +106,14 @@ struct avar
{
static constexpr hb_tag_t tableTag = HB_OT_TAG_avar;
bool has_data () const { return version.to_int (); }
const SegmentMaps* get_segment_maps () const
{ return &firstAxisSegmentMaps; }
unsigned get_axis_count () const
{ return axisCount; }
bool sanitize (hb_sanitize_context_t *c) const
{
TRACE_SANITIZE (this);

View File

@ -96,6 +96,8 @@ struct AxisRecord
info->reserved = 0;
}
hb_tag_t get_axis_tag () const { return axisTag; }
int normalize_axis_value (float v) const
{
float min_value, default_value, max_value;

View File

@ -48,7 +48,10 @@ hb_subset_input_create_or_fail (void)
for (auto& set : input->sets_iter ())
set = hb_set_create ();
if (input->in_error ())
if ((input->axes_location = hb_object_create<hb_hashmap_t<hb_tag_t, float>> ()))
input->axes_location->init_shallow ();
if (!input->axes_location || input->in_error ())
{
hb_subset_input_destroy (input);
return nullptr;
@ -246,6 +249,13 @@ hb_subset_input_destroy (hb_subset_input_t *input)
for (hb_set_t* set : input->sets_iter ())
hb_set_destroy (set);
if (input->axes_location)
{
hb_object_destroy (input->axes_location);
input->axes_location->fini_shallow ();
hb_free (input->axes_location);
}
hb_free (input);
}
@ -376,3 +386,56 @@ hb_subset_input_get_user_data (const hb_subset_input_t *input,
{
return hb_object_get_user_data (input, key);
}
#ifdef HB_EXPERIMENTAL_API
#ifndef HB_NO_VAR
/**
* hb_subset_input_pin_axis_to_default: (skip)
* @input: a #hb_subset_input_t object.
* @axis_tag: Tag of the axis to be pinned
*
* Pin an axis to its default location in the given subset input object.
*
* Return value: `true` if success, `false` otherwise
*
* Since: REPLACEME
**/
hb_bool_t
hb_subset_input_pin_axis_to_default (hb_subset_input_t *input,
hb_face_t *face,
hb_tag_t axis_tag)
{
hb_ot_var_axis_info_t axis_info;
if (!hb_ot_var_find_axis_info (face, axis_tag, &axis_info))
return false;
return input->axes_location->set (axis_tag, axis_info.default_value);
}
/**
* hb_subset_input_pin_axis_location: (skip)
* @input: a #hb_subset_input_t object.
* @axis_tag: Tag of the axis to be pinned
* @axis_value: Location on the axis to be pinned at
*
* Pin an axis to a fixed location in the given subset input object.
*
* Return value: `true` if success, `false` otherwise
*
* Since: REPLACEME
**/
hb_bool_t
hb_subset_input_pin_axis_location (hb_subset_input_t *input,
hb_face_t *face,
hb_tag_t axis_tag,
float axis_value)
{
hb_ot_var_axis_info_t axis_info;
if (!hb_ot_var_find_axis_info (face, axis_tag, &axis_info))
return false;
float val = hb_clamp(axis_value, axis_info.min_value, axis_info.max_value);
return input->axes_location->set (axis_tag, val);
}
#endif
#endif

View File

@ -59,6 +59,7 @@ struct hb_subset_input_t
};
unsigned flags;
hb_hashmap_t<hb_tag_t, float> *axes_location;
inline unsigned num_sets () const
{
@ -77,7 +78,8 @@ struct hb_subset_input_t
if (unlikely (set_ptrs[i]->in_error ()))
return true;
}
return false;
return axes_location->in_error ();
}
};

View File

@ -37,6 +37,7 @@
#include "hb-ot-color-colr-table.hh"
#include "hb-ot-color-colrv1-closure.hh"
#include "hb-ot-var-fvar-table.hh"
#include "hb-ot-var-avar-table.hh"
#include "hb-ot-stat-table.hh"
#include "hb-ot-math-table.hh"
@ -585,6 +586,48 @@ _nameid_closure (hb_face_t *face,
#endif
}
static void
_normalize_axes_location (hb_face_t *face,
const hb_hashmap_t<hb_tag_t, float> *user_axes_location,
hb_hashmap_t<hb_tag_t, int> *normalized_axes_location, /* OUT */
bool &all_axes_pinned)
{
if (user_axes_location->is_empty ())
return;
hb_array_t<const OT::AxisRecord> axes = face->table.fvar->get_axes ();
bool has_avar = face->table.avar->has_data ();
const OT::SegmentMaps *seg_maps = nullptr;
if (has_avar)
seg_maps = face->table.avar->get_segment_maps ();
bool axis_not_pinned = false;
unsigned axis_count = 0;
for (const auto& axis : axes)
{
hb_tag_t axis_tag = axis.get_axis_tag ();
if (!user_axes_location->has (axis_tag))
{
axis_not_pinned = true;
}
else
{
int normalized_v = axis.normalize_axis_value (user_axes_location->get (axis_tag));
if (has_avar && axis_count < face->table.avar->get_axis_count ())
{
normalized_v = seg_maps->map (normalized_v);
}
normalized_axes_location->set (axis_tag, normalized_v);
}
if (has_avar)
seg_maps = &StructAfter<OT::SegmentMaps> (*seg_maps);
axis_count++;
}
all_axes_pinned = !axis_not_pinned;
}
/**
* hb_subset_plan_create_or_fail:
* @face: font face to create the plan for.
@ -636,10 +679,8 @@ hb_subset_plan_create_or_fail (hb_face_t *face,
plan->gsub_lookups = hb_map_create ();
plan->gpos_lookups = hb_map_create ();
if (plan->check_success (plan->gsub_langsys = hb_object_create<script_langsys_map> ()))
plan->gsub_langsys->init_shallow ();
if (plan->check_success (plan->gpos_langsys = hb_object_create<script_langsys_map> ()))
plan->gpos_langsys->init_shallow ();
plan->check_success (plan->gsub_langsys = hb_hashmap_create<unsigned, hb::unique_ptr<hb_set_t>> ());
plan->check_success (plan->gpos_langsys = hb_hashmap_create<unsigned, hb::unique_ptr<hb_set_t>> ());
plan->gsub_features = hb_map_create ();
plan->gpos_features = hb_map_create ();
@ -648,10 +689,9 @@ hb_subset_plan_create_or_fail (hb_face_t *face,
plan->layout_variation_indices = hb_set_create ();
plan->layout_variation_idx_map = hb_map_create ();
if ((plan->sanitized_table_cache = hb_object_create<hb_hashmap_t<hb_tag_t, hb::unique_ptr<hb_blob_t>>> ())) {
plan->sanitized_table_cache->init_shallow ();
}
plan->check_success (plan->sanitized_table_cache = hb_hashmap_create<hb_tag_t, hb::unique_ptr<hb_blob_t>> ());
plan->check_success (plan->axes_location = hb_hashmap_create<hb_tag_t, int> ());
plan->all_axes_pinned = false;
if (unlikely (plan->in_error ())) {
hb_subset_plan_destroy (plan);
@ -685,6 +725,11 @@ hb_subset_plan_create_or_fail (hb_face_t *face,
plan->glyph_map->get(plan->unicode_to_new_gid_list.arrayZ[i].second);
}
_normalize_axes_location (face,
input->axes_location,
plan->axes_location,
plan->all_axes_pinned);
if (unlikely (plan->in_error ())) {
hb_subset_plan_destroy (plan);
return nullptr;
@ -734,25 +779,10 @@ hb_subset_plan_destroy (hb_subset_plan_t *plan)
hb_set_destroy (plan->layout_variation_indices);
hb_map_destroy (plan->layout_variation_idx_map);
if (plan->gsub_langsys)
{
hb_object_destroy (plan->gsub_langsys);
plan->gsub_langsys->fini_shallow ();
hb_free (plan->gsub_langsys);
}
if (plan->gpos_langsys)
{
hb_object_destroy (plan->gpos_langsys);
plan->gpos_langsys->fini_shallow ();
hb_free (plan->gpos_langsys);
}
if (plan->sanitized_table_cache) {
hb_object_destroy (plan->sanitized_table_cache);
plan->sanitized_table_cache->fini ();
hb_free (plan->sanitized_table_cache);
}
hb_hashmap_destroy (plan->gsub_langsys);
hb_hashmap_destroy (plan->gpos_langsys);
hb_hashmap_destroy (plan->axes_location);
hb_hashmap_destroy (plan->sanitized_table_cache);
hb_free (plan);
}

View File

@ -107,6 +107,9 @@ struct hb_subset_plan_t
hb_map_t *layout_variation_idx_map;
hb_hashmap_t<hb_tag_t, hb::unique_ptr<hb_blob_t>>* sanitized_table_cache;
//normalized axes location map
hb_hashmap_t<hb_tag_t, int> *axes_location;
bool all_axes_pinned;
public:

View File

@ -361,6 +361,8 @@ _should_drop_table (hb_subset_plan_t *plan, hb_tag_t tag)
switch (tag)
{
case HB_TAG ('c','v','a','r'): /* hint table, fallthrough */
return plan->all_axes_pinned || (plan->flags & HB_SUBSET_FLAGS_NO_HINTING);
case HB_TAG ('c','v','t',' '): /* hint table, fallthrough */
case HB_TAG ('f','p','g','m'): /* hint table, fallthrough */
case HB_TAG ('p','r','e','p'): /* hint table, fallthrough */
@ -380,6 +382,14 @@ _should_drop_table (hb_subset_plan_t *plan, hb_tag_t tag)
return true;
#endif
case HB_TAG ('a','v','a','r'):
case HB_TAG ('f','v','a','r'):
case HB_TAG ('g','v','a','r'):
case HB_OT_TAG_HVAR:
case HB_OT_TAG_VVAR:
case HB_TAG ('M','V','A','R'):
return plan->all_axes_pinned;
default:
return false;
}

View File

@ -154,6 +154,21 @@ HB_EXTERN void
hb_subset_input_set_flags (hb_subset_input_t *input,
unsigned value);
#ifdef HB_EXPERIMENTAL_API
#ifndef HB_NO_VAR
HB_EXTERN hb_bool_t
hb_subset_input_pin_axis_to_default (hb_subset_input_t *input,
hb_face_t *face,
hb_tag_t axis_tag);
HB_EXTERN hb_bool_t
hb_subset_input_pin_axis_location (hb_subset_input_t *input,
hb_face_t *face,
hb_tag_t axis_tag,
float axis_value);
#endif
#endif
HB_EXTERN hb_face_t *
hb_subset_or_fail (hb_face_t *source, const hb_subset_input_t *input);

View File

@ -648,6 +648,73 @@ parse_drop_tables (const char *name,
return true;
}
#ifdef HB_EXPERIMENTAL_API
#ifndef HB_NO_VAR
static gboolean
parse_instance (const char *name,
const char *arg,
gpointer data,
GError **error)
{
subset_main_t *subset_main = (subset_main_t *) data;
char *s = strtok((char *) arg, "=");
while (s)
{
unsigned len = strlen (s);
if (len > 4) //Axis tags are 4 bytes.
{
g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
"Failed parsing axis tag at: '%s'", s);
return false;
}
hb_tag_t axis_tag = hb_tag_from_string (s, len);
s = strtok(nullptr, ", ");
if (!s)
{
g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
"Value not specified for axis: %c%c%c%c", HB_UNTAG (axis_tag));
return false;
}
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;
}
}
s = strtok(nullptr, "=");
}
return true;
}
#endif
#endif
template <GOptionArgFunc line_parser, bool allow_comments=true>
static gboolean
parse_file_for (const char *name,
@ -818,7 +885,18 @@ subset_main_t::add_options ()
{"drop-tables", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_drop_tables, "Drop the specified tables. Use --drop-tables-=... to subtract from the current set.", "list of string table tags or *"},
{"drop-tables+", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_CALLBACK, (gpointer) &parse_drop_tables, "Drop the specified tables.", "list of string table tags or *"},
{"drop-tables-", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_CALLBACK, (gpointer) &parse_drop_tables, "Drop the specified tables.", "list of string table tags or *"},
#ifdef HB_EXPERIMENTAL_API
#ifndef HB_NO_VAR
{"instance", 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\n"
"number or the literal string 'drop'\n"
" "
"For example: --instance=\"wdth=100 wght=200\" or --instance=\"wdth=drop\"\n"
"Note: currently only fully instancing to the default location is supported\n",
"list of comma separated axis-locations"},
{nullptr}
#endif
#endif
};
add_group (other_entries,
"subset-other",