add option "--instance", store axes_location in subset_plan and drop all
variation tables when all axes are pinned at default
diff --git a/src/gen-def.py b/src/gen-def.py
index 205ed7e..47b7b47 100755
--- a/src/gen-def.py
+++ b/src/gen-def.py
@@ -19,7 +19,9 @@
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)
diff --git a/src/hb-map.hh b/src/hb-map.hh
index df215fc..71d45c1 100644
--- a/src/hb-map.hh
+++ b/src/hb-map.hh
@@ -455,4 +455,28 @@
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 */
diff --git a/src/hb-ot-var-avar-table.hh b/src/hb-ot-var-avar-table.hh
index 65f26c1..71d767e 100644
--- a/src/hb-ot-var-avar-table.hh
+++ b/src/hb-ot-var-avar-table.hh
@@ -106,6 +106,14 @@
{
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);
diff --git a/src/hb-ot-var-fvar-table.hh b/src/hb-ot-var-fvar-table.hh
index c5c476b..6300a42 100644
--- a/src/hb-ot-var-fvar-table.hh
+++ b/src/hb-ot-var-fvar-table.hh
@@ -96,6 +96,8 @@
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;
diff --git a/src/hb-subset-input.cc b/src/hb-subset-input.cc
index 70c33f4..06f9f98 100644
--- a/src/hb-subset-input.cc
+++ b/src/hb-subset-input.cc
@@ -48,7 +48,10 @@
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 @@
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 @@
{
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
diff --git a/src/hb-subset-input.hh b/src/hb-subset-input.hh
index a21a6f3..2335f06 100644
--- a/src/hb-subset-input.hh
+++ b/src/hb-subset-input.hh
@@ -59,6 +59,7 @@
};
unsigned flags;
+ hb_hashmap_t<hb_tag_t, float> *axes_location;
inline unsigned num_sets () const
{
@@ -77,7 +78,8 @@
if (unlikely (set_ptrs[i]->in_error ()))
return true;
}
- return false;
+
+ return axes_location->in_error ();
}
};
diff --git a/src/hb-subset-plan.cc b/src/hb-subset-plan.cc
index 8bda4b5..e405a46 100644
--- a/src/hb-subset-plan.cc
+++ b/src/hb-subset-plan.cc
@@ -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 @@
#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 @@
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 @@
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 @@
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_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);
}
diff --git a/src/hb-subset-plan.hh b/src/hb-subset-plan.hh
index c631ea0..49edacc 100644
--- a/src/hb-subset-plan.hh
+++ b/src/hb-subset-plan.hh
@@ -107,6 +107,9 @@
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:
diff --git a/src/hb-subset.cc b/src/hb-subset.cc
index 34ef923..bcbe73d 100644
--- a/src/hb-subset.cc
+++ b/src/hb-subset.cc
@@ -361,6 +361,8 @@
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 @@
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;
}
diff --git a/src/hb-subset.h b/src/hb-subset.h
index d0064cb..f4346c7 100644
--- a/src/hb-subset.h
+++ b/src/hb-subset.h
@@ -154,6 +154,21 @@
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);
diff --git a/util/hb-subset.cc b/util/hb-subset.cc
index 0592bfa..bca7140 100644
--- a/util/hb-subset.cc
+++ b/util/hb-subset.cc
@@ -648,6 +648,73 @@
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 @@
{"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",