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);
if (!l->visitScript ()) return_trace (false);
if (tag && !c->plan->layout_scripts->has (*tag))
return false;
auto *out = c->serializer->start_embed (*this);
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_markattachclassdef = out->markAttachClassDef.serialize_subset (c, markAttachClassDef, this, nullptr, false, true);
bool subset_markglyphsetsdef = true;
bool subset_markglyphsetsdef = false;
if (version.to_int () >= 0x00010002u)
{
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)
{
subset_varstore = out->varStore.serialize_subset (c, varStore, this);
if (!subset_varstore && version.to_int () == 0x00010003u)
out->version.minor = 2;
}
if (subset_varstore)
{
out->version.minor = 3;
} else if (subset_markglyphsetsdef) {
out->version.minor = 2;
} else {
out->version.minor = 0;
}
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_scripts->invert (); // Default to all scripts.
if (input->in_error ())
{
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_languages;
hb_set_t *layout_features;
hb_set_t *layout_scripts;
};
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 */);
template <typename T>
static void _collect_layout_indices (hb_face_t *face,
const T& table,
const hb_set_t *layout_features_to_retain,
layout_collect_func_t layout_collect_func,
hb_set_t *indices /* 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> features;
if (!features.alloc (table.get_feature_count () + 1))
return;
hb_vector_t<hb_tag_t> out;
out.alloc (tags->get_size() + 1); // +1 is to allocate room for the null terminator.
hb_set_t visited_features;
bool retain_all_features = true;
for (unsigned i = 0; i < table.get_feature_count (); i++)
bool removed = false;
hb_set_t visited;
for (hb_tag_t tag : *tags)
{
hb_tag_t tag = table.get_feature_tag (i);
if (!tag) continue;
if (!layout_features_to_retain->has (tag))
if (visited.has (tag)) continue;
if (!filter->has (tag))
{
retain_all_features = false;
removed = true;
continue;
}
if (visited_features.has (tag))
continue;
features.push (tag);
visited_features.add (tag);
visited.add (tag);
out.push (tag);
}
if (!features)
return;
// The collect function needs a null element to signal end of the array.
features.push (0);
out.push (HB_TAG_NONE);
if (retain_all_features)
{
// Looking for all features, trigger the faster collection method.
layout_collect_func (face,
T::tableTag,
nullptr,
nullptr,
nullptr,
indices);
hb_swap (out, *tags);
return removed;
}
template <typename T>
static void _collect_layout_indices (hb_subset_plan_t *plan,
const T& table,
layout_collect_func_t layout_collect_func,
hb_set_t *indices /* OUT */)
{
unsigned num_features = table.get_feature_count ();
hb_vector_t<hb_tag_t> features;
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;
}
layout_collect_func (face,
layout_collect_func (plan->source,
T::tableTag,
retain_all_scripts ? nullptr : scripts.arrayZ,
nullptr,
nullptr,
features.arrayZ,
retain_all_features ? nullptr : features.arrayZ,
indices);
}
template <typename T>
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,
const hb_set_t *layout_features_to_retain,
hb_map_t *lookups,
hb_map_t *features,
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_set_t lookup_indices;
_collect_layout_indices<T> (face,
_collect_layout_indices<T> (plan,
*table,
layout_features_to_retain,
hb_ot_layout_collect_lookups,
&lookup_indices);
if (table_tag == HB_OT_TAG_GSUB)
hb_ot_layout_lookups_substitute_closure (face,
&lookup_indices,
hb_ot_layout_lookups_substitute_closure (plan->source,
&lookup_indices,
gids_to_retain);
table->closure_lookups (face,
table->closure_lookups (plan->source,
gids_to_retain,
&lookup_indices);
&lookup_indices);
_remap_indexes (&lookup_indices, lookups);
// Collect and prune features
hb_set_t feature_indices;
_collect_layout_indices<T> (face,
_collect_layout_indices<T> (plan,
*table,
layout_features_to_retain,
hb_ot_layout_collect_features,
&feature_indices);
@ -395,18 +401,16 @@ _populate_gids_to_retain (hb_subset_plan_t* plan,
if (close_over_gsub)
// closure all glyphs/lookups/features needed for GSUB substitutions.
_closure_glyphs_lookups_features<GSUB> (
plan->source,
plan,
plan->_glyphset_gsub,
plan->layout_features,
plan->gsub_lookups,
plan->gsub_features,
plan->gsub_langsys);
if (close_over_gpos)
_closure_glyphs_lookups_features<GPOS> (
plan->source,
plan,
plan->_glyphset_gsub,
plan->layout_features,
plan->gpos_lookups,
plan->gpos_features,
plan->gpos_langsys);
@ -549,6 +553,7 @@ hb_subset_plan_create_or_fail (hb_face_t *face,
_nameid_closure (face, plan->name_ids);
plan->name_languages = hb_set_copy (input->sets.name_languages);
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->drop_tables = hb_set_copy (input->sets.drop_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_languages);
hb_set_destroy (plan->layout_features);
hb_set_destroy (plan->layout_scripts);
hb_set_destroy (plan->glyphs_requested);
hb_set_destroy (plan->drop_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
hb_set_t *layout_features;
// layout scripts which will be preserved.
hb_set_t *layout_scripts;
//glyph ids requested to retain
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_LAYOUT_FEATURE_TAG: the set of layout feature tags that will be retained
* 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.
*
@ -113,6 +115,7 @@ typedef enum {
HB_SUBSET_SETS_NAME_ID,
HB_SUBSET_SETS_NAME_LANG_ID,
HB_SUBSET_SETS_LAYOUT_FEATURE_TAG,
HB_SUBSET_SETS_LAYOUT_SCRIPT_TAG,
} hb_subset_sets_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:
default.txt
drop-hints.txt
no-scripts.txt
filter-scripts.txt
filter-scripts-features.txt
filter-scripts-features.2.txt
SUBSETS:
abc

View File

@ -532,30 +532,31 @@ set_flag (const char *name,
}
static gboolean
parse_layout_features (const char *name,
const char *arg,
gpointer data,
GError **error G_GNUC_UNUSED)
parse_layout_tag_list (hb_subset_sets_t set_type,
const char *name,
const char *arg,
gpointer data,
GError **error G_GNUC_UNUSED)
{
subset_main_t *subset_main = (subset_main_t *) data;
hb_bool_t is_remove = (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, "*"))
{
hb_set_clear (layout_features);
hb_set_clear (layout_tags);
if (!is_remove)
hb_set_invert (layout_features);
hb_set_invert (layout_tags);
return true;
}
char *s = strtok((char *) arg, ", ");
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,
"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));
if (!is_remove)
hb_set_add (layout_features, tag);
hb_set_add (layout_tags, tag);
else
hb_set_del (layout_features, tag);
hb_set_del (layout_tags, tag);
s = strtok(nullptr, ", ");
}
@ -575,6 +576,34 @@ parse_layout_features (const char *name,
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
parse_drop_tables (const char *name,
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, 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, 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 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 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, 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 *"},