Merge pull request #3933 from googlefonts/cff
[subset] Fix infinite loop when instancing CFF fonts
This commit is contained in:
commit
51d3ce39ba
|
@ -403,6 +403,9 @@ hb_subset_input_get_user_data (const hb_subset_input_t *input,
|
||||||
*
|
*
|
||||||
* Pin an axis to its default location in the given subset input object.
|
* Pin an axis to its default location in the given subset input object.
|
||||||
*
|
*
|
||||||
|
* Currently only works for fonts with 'glyf' tables. CFF and CFF2 is not
|
||||||
|
* yet supported. Additionally all axes in a font must be pinned.
|
||||||
|
*
|
||||||
* Return value: `true` if success, `false` otherwise
|
* Return value: `true` if success, `false` otherwise
|
||||||
*
|
*
|
||||||
* Since: REPLACEME
|
* Since: REPLACEME
|
||||||
|
@ -427,6 +430,9 @@ hb_subset_input_pin_axis_to_default (hb_subset_input_t *input,
|
||||||
*
|
*
|
||||||
* Pin an axis to a fixed location in the given subset input object.
|
* Pin an axis to a fixed location in the given subset input object.
|
||||||
*
|
*
|
||||||
|
* Currently only works for fonts with 'glyf' tables. CFF and CFF2 is not
|
||||||
|
* yet supported. Additionally all axes in a font must be pinned.
|
||||||
|
*
|
||||||
* Return value: `true` if success, `false` otherwise
|
* Return value: `true` if success, `false` otherwise
|
||||||
*
|
*
|
||||||
* Since: REPLACEME
|
* Since: REPLACEME
|
||||||
|
|
|
@ -413,20 +413,14 @@ _passthrough (hb_subset_plan_t *plan, hb_tag_t tag)
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
_dependencies_satisfied (hb_subset_plan_t *plan, hb_tag_t tag,
|
_dependencies_satisfied (hb_subset_plan_t *plan, hb_tag_t tag,
|
||||||
hb_set_t &visited_set, hb_set_t &revisit_set)
|
const hb_set_t &subsetted_tags,
|
||||||
|
const hb_set_t &pending_subset_tags)
|
||||||
{
|
{
|
||||||
switch (tag)
|
switch (tag)
|
||||||
{
|
{
|
||||||
case HB_OT_TAG_hmtx:
|
case HB_OT_TAG_hmtx:
|
||||||
case HB_OT_TAG_vmtx:
|
case HB_OT_TAG_vmtx:
|
||||||
if (!plan->pinned_at_default &&
|
return plan->pinned_at_default || !pending_subset_tags.has (HB_OT_TAG_glyf);
|
||||||
!visited_set.has (HB_OT_TAG_glyf))
|
|
||||||
{
|
|
||||||
revisit_set.add (tag);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -572,41 +566,58 @@ hb_subset_plan_execute_or_fail (hb_subset_plan_t *plan)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
hb_set_t tags_set, revisit_set;
|
|
||||||
bool success = true;
|
|
||||||
hb_tag_t table_tags[32];
|
hb_tag_t table_tags[32];
|
||||||
unsigned offset = 0, num_tables = ARRAY_LENGTH (table_tags);
|
unsigned offset = 0, num_tables = ARRAY_LENGTH (table_tags);
|
||||||
hb_vector_t<char> buf;
|
|
||||||
buf.alloc (4096 - 16);
|
|
||||||
|
|
||||||
|
hb_set_t subsetted_tags, pending_subset_tags;
|
||||||
while (((void) _get_table_tags (plan, offset, &num_tables, table_tags), num_tables))
|
while (((void) _get_table_tags (plan, offset, &num_tables, table_tags), num_tables))
|
||||||
{
|
{
|
||||||
for (unsigned i = 0; i < num_tables; ++i)
|
for (unsigned i = 0; i < num_tables; ++i)
|
||||||
{
|
{
|
||||||
hb_tag_t tag = table_tags[i];
|
hb_tag_t tag = table_tags[i];
|
||||||
if (_should_drop_table (plan, tag) && !tags_set.has (tag)) continue;
|
if (_should_drop_table (plan, tag)) continue;
|
||||||
if (!_dependencies_satisfied (plan, tag, tags_set, revisit_set)) continue;
|
pending_subset_tags.add (tag);
|
||||||
tags_set.add (tag);
|
}
|
||||||
|
|
||||||
|
offset += num_tables;
|
||||||
|
}
|
||||||
|
|
||||||
|
hb_vector_t<char> buf;
|
||||||
|
buf.alloc (4096 - 16);
|
||||||
|
|
||||||
|
|
||||||
|
bool success = true;
|
||||||
|
|
||||||
|
while (!pending_subset_tags.is_empty ())
|
||||||
|
{
|
||||||
|
bool made_changes = false;
|
||||||
|
for (hb_tag_t tag : pending_subset_tags)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (!_dependencies_satisfied (plan, tag,
|
||||||
|
subsetted_tags,
|
||||||
|
pending_subset_tags))
|
||||||
|
{
|
||||||
|
// delayed subsetting for some tables since they might have dependency on other tables
|
||||||
|
// in some cases: e.g: during instantiating glyf tables, hmetrics/vmetrics are updated
|
||||||
|
// and saved in subset plan, hmtx/vmtx subsetting need to use these updated metrics values
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
pending_subset_tags.del (tag);
|
||||||
|
subsetted_tags.add (tag);
|
||||||
|
made_changes = true;
|
||||||
|
|
||||||
success = _subset_table (plan, buf, tag);
|
success = _subset_table (plan, buf, tag);
|
||||||
if (unlikely (!success)) goto end;
|
if (unlikely (!success)) goto end;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*delayed subsetting for some tables since they might have dependency on other tables in some cases:
|
if (!made_changes)
|
||||||
e.g: during instantiating glyf tables, hmetrics/vmetrics are updated and saved in subset plan,
|
|
||||||
hmtx/vmtx subsetting need to use these updated metrics values*/
|
|
||||||
while (!revisit_set.is_empty ())
|
|
||||||
{
|
{
|
||||||
hb_set_t revisit_temp;
|
DEBUG_MSG (SUBSET, nullptr, "Table dependencies unable to be satisfied. Subset failed.");
|
||||||
for (hb_tag_t tag : revisit_set)
|
success = false;
|
||||||
{
|
goto end;
|
||||||
if (!_dependencies_satisfied (plan, tag, tags_set, revisit_temp)) continue;
|
|
||||||
tags_set.add (tag);
|
|
||||||
success = _subset_table (plan, buf, tag);
|
|
||||||
if (unlikely (!success)) goto end;
|
|
||||||
}
|
}
|
||||||
revisit_set = revisit_temp;
|
|
||||||
}
|
|
||||||
offset += num_tables;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (success && plan->attach_accelerator_data) {
|
if (success && plan->attach_accelerator_data) {
|
||||||
|
|
|
@ -47,6 +47,10 @@ TESTS = \
|
||||||
tests/math.tests \
|
tests/math.tests \
|
||||||
tests/math_coverage_offset.tests \
|
tests/math_coverage_offset.tests \
|
||||||
tests/post.tests \
|
tests/post.tests \
|
||||||
|
tests/full_instance.tests \
|
||||||
|
tests/instance_feature_variations.tests \
|
||||||
|
tests/instantiate_glyf.tests \
|
||||||
|
tests/pin_all_at_default.tests \
|
||||||
$(NULL)
|
$(NULL)
|
||||||
|
|
||||||
# TODO: re-enable once colrv1 subsetting is stabilized.
|
# TODO: re-enable once colrv1 subsetting is stabilized.
|
||||||
|
@ -59,8 +63,4 @@ XFAIL_TESTS = \
|
||||||
|
|
||||||
# Disabled because instancing is only available w/ experimental API on.
|
# Disabled because instancing is only available w/ experimental API on.
|
||||||
DISABLED_TESTS = \
|
DISABLED_TESTS = \
|
||||||
tests/full_instance.tests \
|
|
||||||
tests/instance_feature_variations.tests \
|
|
||||||
tests/instantiate_glyf.tests \
|
|
||||||
tests/pin_all_at_default.tests \
|
|
||||||
$(NULL)
|
$(NULL)
|
||||||
|
|
|
@ -35,7 +35,6 @@ def generate_expected_output(input_file, unicodes, profile_flags, instance_flags
|
||||||
"--no-overlap-flag",
|
"--no-overlap-flag",
|
||||||
"--no-recalc-bounds",
|
"--no-recalc-bounds",
|
||||||
"--no-recalc-timestamp",
|
"--no-recalc-timestamp",
|
||||||
"--no-harfbuzz-repacker", # disable harfbuzz repacker so we aren't comparing to ourself.
|
|
||||||
"--output=%s" % instance_path,
|
"--output=%s" % instance_path,
|
||||||
input_file]
|
input_file]
|
||||||
args.extend(instance_flags)
|
args.extend(instance_flags)
|
||||||
|
|
|
@ -49,17 +49,11 @@ tests = [
|
||||||
'glyph_names',
|
'glyph_names',
|
||||||
'post',
|
'post',
|
||||||
'32bit_var_store',
|
'32bit_var_store',
|
||||||
]
|
|
||||||
|
|
||||||
if get_option('experimental_api')
|
|
||||||
# instacing tests, only supported in experimental api
|
|
||||||
tests += [
|
|
||||||
'pin_all_at_default',
|
'pin_all_at_default',
|
||||||
'instantiate_glyf',
|
'instantiate_glyf',
|
||||||
'full_instance',
|
'full_instance',
|
||||||
'instance_feature_variations',
|
'instance_feature_variations',
|
||||||
]
|
]
|
||||||
endif
|
|
||||||
|
|
||||||
repack_tests = [
|
repack_tests = [
|
||||||
'basic',
|
'basic',
|
||||||
|
@ -70,7 +64,6 @@ repack_tests = [
|
||||||
'space_splitting',
|
'space_splitting',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
run_test = find_program('run-tests.py')
|
run_test = find_program('run-tests.py')
|
||||||
|
|
||||||
foreach t : tests
|
foreach t : tests
|
||||||
|
|
|
@ -660,7 +660,6 @@ parse_drop_tables (const char *name,
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef HB_EXPERIMENTAL_API
|
|
||||||
#ifndef HB_NO_VAR
|
#ifndef HB_NO_VAR
|
||||||
static gboolean
|
static gboolean
|
||||||
parse_instance (const char *name,
|
parse_instance (const char *name,
|
||||||
|
@ -725,7 +724,6 @@ parse_instance (const char *name,
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#endif
|
|
||||||
|
|
||||||
template <GOptionArgFunc line_parser, bool allow_comments=true>
|
template <GOptionArgFunc line_parser, bool allow_comments=true>
|
||||||
static gboolean
|
static gboolean
|
||||||
|
@ -897,7 +895,6 @@ 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, 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 *"},
|
||||||
{"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
|
#ifndef HB_NO_VAR
|
||||||
{"instance", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_instance,
|
{"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"
|
"(Partially|Fully) Instantiate a variable font. A location consists of the tag of a variation axis, followed by '=', followed by a\n"
|
||||||
|
@ -906,7 +903,6 @@ subset_main_t::add_options ()
|
||||||
"For example: --instance=\"wdth=100 wght=200\" or --instance=\"wdth=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",
|
"Note: currently only fully instancing to the default location is supported\n",
|
||||||
"list of comma separated axis-locations"},
|
"list of comma separated axis-locations"},
|
||||||
#endif
|
|
||||||
#endif
|
#endif
|
||||||
{nullptr}
|
{nullptr}
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue