Merge pull request #3699 from googlefonts/filter_scripts

[subset] Add support for --layout-scripts
This commit is contained in:
Behdad Esfahbod 2022-07-05 12:46:05 -06:00 committed by GitHub
commit 386e1bbad8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
45 changed files with 138 additions and 72 deletions

View File

@ -764,6 +764,8 @@ struct Script
{ {
TRACE_SUBSET (this); TRACE_SUBSET (this);
if (!l->visitScript ()) return_trace (false); if (!l->visitScript ()) return_trace (false);
if (tag && !c->plan->layout_scripts->has (*tag))
return false;
auto *out = c->serializer->start_embed (*this); auto *out = c->serializer->start_embed (*this);
if (unlikely (!out || !c->serializer->extend_min (out))) return_trace (false); if (unlikely (!out || !c->serializer->extend_min (out))) return_trace (false);

View File

@ -645,21 +645,25 @@ struct GDEF
bool subset_ligcaretlist = out->ligCaretList.serialize_subset (c, ligCaretList, this); bool subset_ligcaretlist = out->ligCaretList.serialize_subset (c, ligCaretList, this);
bool subset_markattachclassdef = out->markAttachClassDef.serialize_subset (c, markAttachClassDef, this, nullptr, false, true); bool subset_markattachclassdef = out->markAttachClassDef.serialize_subset (c, markAttachClassDef, this, nullptr, false, true);
bool subset_markglyphsetsdef = true; bool subset_markglyphsetsdef = false;
if (version.to_int () >= 0x00010002u) if (version.to_int () >= 0x00010002u)
{ {
subset_markglyphsetsdef = out->markGlyphSetsDef.serialize_subset (c, markGlyphSetsDef, this); subset_markglyphsetsdef = out->markGlyphSetsDef.serialize_subset (c, markGlyphSetsDef, this);
if (!subset_markglyphsetsdef &&
version.to_int () == 0x00010002u)
out->version.minor = 0;
} }
bool subset_varstore = true; bool subset_varstore = false;
if (version.to_int () >= 0x00010003u) if (version.to_int () >= 0x00010003u)
{ {
subset_varstore = out->varStore.serialize_subset (c, varStore, this); subset_varstore = out->varStore.serialize_subset (c, varStore, this);
if (!subset_varstore && version.to_int () == 0x00010003u) }
if (subset_varstore)
{
out->version.minor = 3;
} else if (subset_markglyphsetsdef) {
out->version.minor = 2; out->version.minor = 2;
} else {
out->version.minor = 0;
} }
return_trace (subset_glyphclassdef || subset_attachlist || return_trace (subset_glyphclassdef || subset_attachlist ||

View File

@ -203,6 +203,8 @@ hb_subset_input_create_or_fail (void)
input->sets.layout_features->add_array (default_layout_features, ARRAY_LENGTH (default_layout_features)); input->sets.layout_features->add_array (default_layout_features, ARRAY_LENGTH (default_layout_features));
input->sets.layout_scripts->invert (); // Default to all scripts.
if (input->in_error ()) if (input->in_error ())
{ {
hb_subset_input_destroy (input); hb_subset_input_destroy (input);

View File

@ -50,6 +50,7 @@ struct hb_subset_input_t
hb_set_t *name_ids; hb_set_t *name_ids;
hb_set_t *name_languages; hb_set_t *name_languages;
hb_set_t *layout_features; hb_set_t *layout_features;
hb_set_t *layout_scripts;
}; };
union { union {

View File

@ -91,94 +91,100 @@ _remap_indexes (const hb_set_t *indexes,
typedef void (*layout_collect_func_t) (hb_face_t *face, hb_tag_t table_tag, const hb_tag_t *scripts, const hb_tag_t *languages, const hb_tag_t *features, hb_set_t *lookup_indexes /* OUT */); typedef void (*layout_collect_func_t) (hb_face_t *face, hb_tag_t table_tag, const hb_tag_t *scripts, const hb_tag_t *languages, const hb_tag_t *features, hb_set_t *lookup_indexes /* OUT */);
/*
* Removes all tags from 'tags' that are not in filter. Additionally eliminates any duplicates.
* Returns true if anything was removed (not including duplicates).
*/
static bool _filter_tag_list(hb_vector_t<hb_tag_t>* tags, /* IN/OUT */
const hb_set_t* filter)
{
hb_vector_t<hb_tag_t> out;
out.alloc (tags->get_size() + 1); // +1 is to allocate room for the null terminator.
bool removed = false;
hb_set_t visited;
for (hb_tag_t tag : *tags)
{
if (!tag) continue;
if (visited.has (tag)) continue;
if (!filter->has (tag))
{
removed = true;
continue;
}
visited.add (tag);
out.push (tag);
}
// The collect function needs a null element to signal end of the array.
out.push (HB_TAG_NONE);
hb_swap (out, *tags);
return removed;
}
template <typename T> template <typename T>
static void _collect_layout_indices (hb_face_t *face, static void _collect_layout_indices (hb_subset_plan_t *plan,
const T& table, const T& table,
const hb_set_t *layout_features_to_retain,
layout_collect_func_t layout_collect_func, layout_collect_func_t layout_collect_func,
hb_set_t *indices /* OUT */) hb_set_t *indices /* OUT */)
{ {
unsigned num_features = table.get_feature_count ();
hb_vector_t<hb_tag_t> features; hb_vector_t<hb_tag_t> features;
if (!features.alloc (table.get_feature_count () + 1)) if (!plan->check_success (features.resize (num_features))) return;
table.get_feature_tags (0, &num_features, features.arrayZ);
bool retain_all_features = !_filter_tag_list (&features, plan->layout_features);
unsigned num_scripts = table.get_script_count ();
hb_vector_t<hb_tag_t> scripts;
if (!plan->check_success (scripts.resize (num_scripts))) return;
table.get_script_tags (0, &num_scripts, scripts.arrayZ);
bool retain_all_scripts = !_filter_tag_list (&scripts, plan->layout_scripts);
if (!plan->check_success (!features.in_error ()) || !features
|| !plan->check_success (!scripts.in_error ()) || !scripts)
return; return;
hb_set_t visited_features; layout_collect_func (plan->source,
bool retain_all_features = true;
for (unsigned i = 0; i < table.get_feature_count (); i++)
{
hb_tag_t tag = table.get_feature_tag (i);
if (!tag) continue;
if (!layout_features_to_retain->has (tag))
{
retain_all_features = false;
continue;
}
if (visited_features.has (tag))
continue;
features.push (tag);
visited_features.add (tag);
}
if (!features)
return;
// The collect function needs a null element to signal end of the array.
features.push (0);
if (retain_all_features)
{
// Looking for all features, trigger the faster collection method.
layout_collect_func (face,
T::tableTag, T::tableTag,
retain_all_scripts ? nullptr : scripts.arrayZ,
nullptr, nullptr,
nullptr, retain_all_features ? nullptr : features.arrayZ,
nullptr,
indices);
return;
}
layout_collect_func (face,
T::tableTag,
nullptr,
nullptr,
features.arrayZ,
indices); indices);
} }
template <typename T> template <typename T>
static inline void static inline void
_closure_glyphs_lookups_features (hb_face_t *face, _closure_glyphs_lookups_features (hb_subset_plan_t *plan,
hb_set_t *gids_to_retain, hb_set_t *gids_to_retain,
const hb_set_t *layout_features_to_retain,
hb_map_t *lookups, hb_map_t *lookups,
hb_map_t *features, hb_map_t *features,
script_langsys_map *langsys_map) script_langsys_map *langsys_map)
{ {
hb_blob_ptr_t<T> table = hb_sanitize_context_t ().reference_table<T> (face); hb_blob_ptr_t<T> table = hb_sanitize_context_t ().reference_table<T> (plan->source);
hb_tag_t table_tag = table->tableTag; hb_tag_t table_tag = table->tableTag;
hb_set_t lookup_indices; hb_set_t lookup_indices;
_collect_layout_indices<T> (face, _collect_layout_indices<T> (plan,
*table, *table,
layout_features_to_retain,
hb_ot_layout_collect_lookups, hb_ot_layout_collect_lookups,
&lookup_indices); &lookup_indices);
if (table_tag == HB_OT_TAG_GSUB) if (table_tag == HB_OT_TAG_GSUB)
hb_ot_layout_lookups_substitute_closure (face, hb_ot_layout_lookups_substitute_closure (plan->source,
&lookup_indices, &lookup_indices,
gids_to_retain); gids_to_retain);
table->closure_lookups (face, table->closure_lookups (plan->source,
gids_to_retain, gids_to_retain,
&lookup_indices); &lookup_indices);
_remap_indexes (&lookup_indices, lookups); _remap_indexes (&lookup_indices, lookups);
// Collect and prune features // Collect and prune features
hb_set_t feature_indices; hb_set_t feature_indices;
_collect_layout_indices<T> (face, _collect_layout_indices<T> (plan,
*table, *table,
layout_features_to_retain,
hb_ot_layout_collect_features, hb_ot_layout_collect_features,
&feature_indices); &feature_indices);
@ -395,18 +401,16 @@ _populate_gids_to_retain (hb_subset_plan_t* plan,
if (close_over_gsub) if (close_over_gsub)
// closure all glyphs/lookups/features needed for GSUB substitutions. // closure all glyphs/lookups/features needed for GSUB substitutions.
_closure_glyphs_lookups_features<GSUB> ( _closure_glyphs_lookups_features<GSUB> (
plan->source, plan,
plan->_glyphset_gsub, plan->_glyphset_gsub,
plan->layout_features,
plan->gsub_lookups, plan->gsub_lookups,
plan->gsub_features, plan->gsub_features,
plan->gsub_langsys); plan->gsub_langsys);
if (close_over_gpos) if (close_over_gpos)
_closure_glyphs_lookups_features<GPOS> ( _closure_glyphs_lookups_features<GPOS> (
plan->source, plan,
plan->_glyphset_gsub, plan->_glyphset_gsub,
plan->layout_features,
plan->gpos_lookups, plan->gpos_lookups,
plan->gpos_features, plan->gpos_features,
plan->gpos_langsys); plan->gpos_langsys);
@ -549,6 +553,7 @@ hb_subset_plan_create_or_fail (hb_face_t *face,
_nameid_closure (face, plan->name_ids); _nameid_closure (face, plan->name_ids);
plan->name_languages = hb_set_copy (input->sets.name_languages); plan->name_languages = hb_set_copy (input->sets.name_languages);
plan->layout_features = hb_set_copy (input->sets.layout_features); plan->layout_features = hb_set_copy (input->sets.layout_features);
plan->layout_scripts = hb_set_copy (input->sets.layout_scripts);
plan->glyphs_requested = hb_set_copy (input->sets.glyphs); plan->glyphs_requested = hb_set_copy (input->sets.glyphs);
plan->drop_tables = hb_set_copy (input->sets.drop_tables); plan->drop_tables = hb_set_copy (input->sets.drop_tables);
plan->no_subset_tables = hb_set_copy (input->sets.no_subset_tables); plan->no_subset_tables = hb_set_copy (input->sets.no_subset_tables);
@ -636,6 +641,7 @@ hb_subset_plan_destroy (hb_subset_plan_t *plan)
hb_set_destroy (plan->name_ids); hb_set_destroy (plan->name_ids);
hb_set_destroy (plan->name_languages); hb_set_destroy (plan->name_languages);
hb_set_destroy (plan->layout_features); hb_set_destroy (plan->layout_features);
hb_set_destroy (plan->layout_scripts);
hb_set_destroy (plan->glyphs_requested); hb_set_destroy (plan->glyphs_requested);
hb_set_destroy (plan->drop_tables); hb_set_destroy (plan->drop_tables);
hb_set_destroy (plan->no_subset_tables); hb_set_destroy (plan->no_subset_tables);

View File

@ -55,6 +55,9 @@ struct hb_subset_plan_t
//layout features which will be preserved //layout features which will be preserved
hb_set_t *layout_features; hb_set_t *layout_features;
// layout scripts which will be preserved.
hb_set_t *layout_scripts;
//glyph ids requested to retain //glyph ids requested to retain
hb_set_t *glyphs_requested; hb_set_t *glyphs_requested;

View File

@ -100,6 +100,8 @@ typedef enum { /*< flags >*/
* @HB_SUBSET_SETS_NAME_LANG_ID: the set of name lang ids that will be retained. * @HB_SUBSET_SETS_NAME_LANG_ID: the set of name lang ids that will be retained.
* @HB_SUBSET_SETS_LAYOUT_FEATURE_TAG: the set of layout feature tags that will be retained * @HB_SUBSET_SETS_LAYOUT_FEATURE_TAG: the set of layout feature tags that will be retained
* in the subset. * in the subset.
* @HB_SUBSET_SETS_LAYOUT_SCRIPT_TAG: the set of layout script tags that will be retained
* in the subset. Defaults to all tags. Since: REPLACEME
* *
* List of sets that can be configured on the subset input. * List of sets that can be configured on the subset input.
* *
@ -113,6 +115,7 @@ typedef enum {
HB_SUBSET_SETS_NAME_ID, HB_SUBSET_SETS_NAME_ID,
HB_SUBSET_SETS_NAME_LANG_ID, HB_SUBSET_SETS_NAME_LANG_ID,
HB_SUBSET_SETS_LAYOUT_FEATURE_TAG, HB_SUBSET_SETS_LAYOUT_FEATURE_TAG,
HB_SUBSET_SETS_LAYOUT_SCRIPT_TAG,
} hb_subset_sets_t; } hb_subset_sets_t;
HB_EXTERN hb_subset_input_t * HB_EXTERN hb_subset_input_t *

View File

@ -0,0 +1,2 @@
--layout-scripts=grek,latn
--layout-features=liga

View File

@ -0,0 +1,2 @@
--layout-scripts=grek,cyrl
--layout-features=numr,onum

View File

@ -0,0 +1 @@
--layout-scripts=grek,cyrl

View File

@ -0,0 +1 @@
--layout-scripts-=*

View File

@ -5,6 +5,10 @@ SourceSerifVariable-Roman.ttf
PROFILES: PROFILES:
default.txt default.txt
drop-hints.txt drop-hints.txt
no-scripts.txt
filter-scripts.txt
filter-scripts-features.txt
filter-scripts-features.2.txt
SUBSETS: SUBSETS:
abc abc

View File

@ -532,7 +532,8 @@ set_flag (const char *name,
} }
static gboolean static gboolean
parse_layout_features (const char *name, parse_layout_tag_list (hb_subset_sets_t set_type,
const char *name,
const char *arg, const char *arg,
gpointer data, gpointer data,
GError **error G_GNUC_UNUSED) GError **error G_GNUC_UNUSED)
@ -540,22 +541,22 @@ parse_layout_features (const char *name,
subset_main_t *subset_main = (subset_main_t *) data; subset_main_t *subset_main = (subset_main_t *) data;
hb_bool_t is_remove = (name[strlen (name) - 1] == '-'); hb_bool_t is_remove = (name[strlen (name) - 1] == '-');
hb_bool_t is_add = (name[strlen (name) - 1] == '+'); hb_bool_t is_add = (name[strlen (name) - 1] == '+');
hb_set_t *layout_features = hb_subset_input_set (subset_main->input, HB_SUBSET_SETS_LAYOUT_FEATURE_TAG); hb_set_t *layout_tags = hb_subset_input_set (subset_main->input, set_type);
if (!is_remove && !is_add) hb_set_clear (layout_features); if (!is_remove && !is_add) hb_set_clear (layout_tags);
if (0 == strcmp (arg, "*")) if (0 == strcmp (arg, "*"))
{ {
hb_set_clear (layout_features); hb_set_clear (layout_tags);
if (!is_remove) if (!is_remove)
hb_set_invert (layout_features); hb_set_invert (layout_tags);
return true; return true;
} }
char *s = strtok((char *) arg, ", "); char *s = strtok((char *) arg, ", ");
while (s) while (s)
{ {
if (strlen (s) > 4) // table tags are at most 4 bytes if (strlen (s) > 4) // tags are at most 4 bytes
{ {
g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE, g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE,
"Failed parsing table tag at: '%s'", s); "Failed parsing table tag at: '%s'", s);
@ -565,9 +566,9 @@ parse_layout_features (const char *name,
hb_tag_t tag = hb_tag_from_string (s, strlen (s)); hb_tag_t tag = hb_tag_from_string (s, strlen (s));
if (!is_remove) if (!is_remove)
hb_set_add (layout_features, tag); hb_set_add (layout_tags, tag);
else else
hb_set_del (layout_features, tag); hb_set_del (layout_tags, tag);
s = strtok(nullptr, ", "); s = strtok(nullptr, ", ");
} }
@ -575,6 +576,34 @@ parse_layout_features (const char *name,
return true; return true;
} }
static gboolean
parse_layout_features (const char *name,
const char *arg,
gpointer data,
GError **error)
{
return parse_layout_tag_list (HB_SUBSET_SETS_LAYOUT_FEATURE_TAG,
name,
arg,
data,
error);
}
static gboolean
parse_layout_scripts (const char *name,
const char *arg,
gpointer data,
GError **error)
{
return parse_layout_tag_list (HB_SUBSET_SETS_LAYOUT_SCRIPT_TAG,
name,
arg,
data,
error);
}
static gboolean static gboolean
parse_drop_tables (const char *name, parse_drop_tables (const char *name,
const char *arg, const char *arg,
@ -777,9 +806,15 @@ subset_main_t::add_options ()
{"name-languages", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_name_languages, "Subset nameRecords with specified language IDs. Use --name-languages-=... to substract from the current set.", "list of int numbers or *"}, {"name-languages", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_name_languages, "Subset nameRecords with specified language IDs. Use --name-languages-=... to substract from the current set.", "list of int numbers or *"},
{"name-languages-", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_CALLBACK, (gpointer) &parse_name_languages, "Subset nameRecords with specified language IDs", "list of int numbers or *"}, {"name-languages-", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_CALLBACK, (gpointer) &parse_name_languages, "Subset nameRecords with specified language IDs", "list of int numbers or *"},
{"name-languages+", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_CALLBACK, (gpointer) &parse_name_languages, "Subset nameRecords with specified language IDs", "list of int numbers or *"}, {"name-languages+", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_CALLBACK, (gpointer) &parse_name_languages, "Subset nameRecords with specified language IDs", "list of int numbers or *"},
{"layout-features", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_layout_features, "Specify set of layout feature tags that will be preserved. Use --layout-features-=... to substract from the current set.", "list of string table tags or *"}, {"layout-features", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_layout_features, "Specify set of layout feature tags that will be preserved. Use --layout-features-=... to substract from the current set.", "list of string table tags or *"},
{"layout-features+",0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_CALLBACK, (gpointer) &parse_layout_features, "Specify set of layout feature tags that will be preserved", "list of string table tags or *"}, {"layout-features+",0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_CALLBACK, (gpointer) &parse_layout_features, "Specify set of layout feature tags that will be preserved", "list of string tags or *"},
{"layout-features-",0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_CALLBACK, (gpointer) &parse_layout_features, "Specify set of layout feature tags that will be preserved", "list of string table tags or *"}, {"layout-features-",0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_CALLBACK, (gpointer) &parse_layout_features, "Specify set of layout feature tags that will be preserved", "list of string tags or *"},
{"layout-scripts", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_layout_scripts, "Specify set of layout script tags that will be preserved. Use --layout-scripts-=... to substract from the current set.", "list of string table tags or *"},
{"layout-scripts+",0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_CALLBACK, (gpointer) &parse_layout_scripts, "Specify set of layout script tags that will be preserved", "list of string tags or *"},
{"layout-scripts-",0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_CALLBACK, (gpointer) &parse_layout_scripts, "Specify set of layout script tags that will be preserved", "list of string tags or *"},
{"drop-tables", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_drop_tables, "Drop the specified tables. Use --drop-tables-=... to substract 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 substract 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 *"},