Merge pull request #3610 from googlefonts/subset_create_tables_face

[subset] fix subsetting of faces created via hb_face_create_for_tables.
This commit is contained in:
Behdad Esfahbod 2022-05-26 05:16:07 -06:00 committed by GitHub
commit 67bd147c73
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 150 additions and 2 deletions

View File

@ -78,6 +78,108 @@ using OT::Layout::GSUB::GSUB;
* retain glyph ids option and configure the subset to pass through the layout tables untouched.
*/
/*
* The list of tables in the open type spec. Used to check for tables that may need handling
* if we are unable to list the tables in a face.
*/
static hb_tag_t known_tables[] {
HB_TAG ('a', 'v', 'a', 'r'),
HB_OT_TAG_BASE,
HB_OT_TAG_CBDT,
HB_OT_TAG_CBLC,
HB_OT_TAG_cff1,
HB_OT_TAG_cff2,
HB_OT_TAG_cmap,
HB_OT_TAG_COLR,
HB_OT_TAG_CPAL,
HB_TAG ('c', 'v', 'a', 'r'),
HB_TAG ('c', 'v', 't', ' '),
HB_TAG ('D', 'S', 'I', 'G'),
HB_TAG ('E', 'B', 'D', 'T'),
HB_TAG ('E', 'B', 'L', 'C'),
HB_TAG ('E', 'B', 'S', 'C'),
HB_TAG ('f', 'p', 'g', 'm'),
HB_TAG ('f', 'v', 'a', 'r'),
HB_TAG ('g', 'a', 's', 'p'),
HB_OT_TAG_GDEF,
HB_OT_TAG_glyf,
HB_OT_TAG_GPOS,
HB_OT_TAG_GSUB,
HB_OT_TAG_gvar,
HB_OT_TAG_hdmx,
HB_OT_TAG_head,
HB_OT_TAG_hhea,
HB_OT_TAG_hmtx,
HB_OT_TAG_HVAR,
HB_OT_TAG_JSTF,
HB_TAG ('k', 'e', 'r', 'n'),
HB_OT_TAG_loca,
HB_TAG ('L', 'T', 'S', 'H'),
HB_OT_TAG_MATH,
HB_OT_TAG_maxp,
HB_TAG ('M', 'E', 'R', 'G'),
HB_TAG ('m', 'e', 't', 'a'),
HB_TAG ('M', 'V', 'A', 'R'),
HB_TAG ('P', 'C', 'L', 'T'),
HB_OT_TAG_post,
HB_TAG ('p', 'r', 'e', 'p'),
HB_OT_TAG_sbix,
HB_TAG ('S', 'T', 'A', 'T'),
HB_TAG ('S', 'V', 'G', ' '),
HB_TAG ('V', 'D', 'M', 'X'),
HB_OT_TAG_vhea,
HB_OT_TAG_vmtx,
HB_OT_TAG_VORG,
HB_OT_TAG_VVAR,
HB_OT_TAG_name,
HB_OT_TAG_OS2
};
static bool _table_is_empty (const hb_face_t *face, hb_tag_t tag)
{
hb_blob_t* blob = hb_face_reference_table (face, tag);
bool result = (blob == hb_blob_get_empty ());
hb_blob_destroy (blob);
return result;
}
static unsigned int
_get_table_tags (const hb_subset_plan_t* plan,
unsigned int start_offset,
unsigned int *table_count, /* IN/OUT */
hb_tag_t *table_tags /* OUT */)
{
unsigned num_tables = hb_face_get_table_tags (plan->source, 0, nullptr, nullptr);
if (num_tables)
return hb_face_get_table_tags (plan->source, start_offset, table_count, table_tags);
// If face has 0 tables associated with it, assume that it was built from
// hb_face_create_tables and thus is unable to list its tables. Fallback to
// checking each table type we can handle for existence instead.
auto it =
hb_concat (
+ hb_array (known_tables)
| hb_filter ([&] (hb_tag_t tag) {
return !_table_is_empty (plan->source, tag) && !plan->no_subset_tables->has (tag);
})
| hb_map ([] (hb_tag_t tag) -> hb_tag_t { return tag; }),
plan->no_subset_tables->iter ()
| hb_filter([&] (hb_tag_t tag) {
return !_table_is_empty (plan->source, tag);
}));
it += start_offset;
unsigned num_written = 0;
while (bool (it) && num_written < *table_count)
table_tags[num_written++] = *it++;
*table_count = num_written;
return num_written;
}
static unsigned
_plan_estimate_subset_table_size (hb_subset_plan_t *plan,
unsigned table_len,
@ -224,6 +326,14 @@ _subset (hb_subset_plan_t *plan, hb_vector_t<char> &buf)
static bool
_is_table_present (hb_face_t *source, hb_tag_t tag)
{
if (!hb_face_get_table_tags (source, 0, nullptr, nullptr)) {
// If face has 0 tables associated with it, assume that it was built from
// hb_face_create_tables and thus is unable to list its tables. Fallback to
// checking if the blob associated with tag is empty.
return !_table_is_empty (source, tag);
}
hb_tag_t table_tags[32];
unsigned offset = 0, num_tables = ARRAY_LENGTH (table_tags);
while ((hb_face_get_table_tags (source, offset, &num_tables, table_tags), num_tables))
@ -388,7 +498,8 @@ hb_subset_plan_execute_or_fail (hb_subset_plan_t *plan)
unsigned offset = 0, num_tables = ARRAY_LENGTH (table_tags);
hb_vector_t<char> buf;
buf.alloc (4096 - 16);
while ((hb_face_get_table_tags (plan->source, offset, &num_tables, table_tags), num_tables))
while ((_get_table_tags (plan, offset, &num_tables, table_tags), num_tables))
{
for (unsigned i = 0; i < num_tables; ++i)
{
@ -400,7 +511,7 @@ hb_subset_plan_execute_or_fail (hb_subset_plan_t *plan)
}
offset += num_tables;
}
end:
end:
return success ? hb_face_reference (plan->dest) : nullptr;
}

View File

@ -193,6 +193,42 @@ test_subset_plan (void)
hb_face_destroy (face_ac);
}
static hb_blob_t*
_ref_table (hb_face_t *face, hb_tag_t tag, void *user_data)
{
return hb_face_reference_table ((hb_face_t*) user_data, tag);
}
static void
test_subset_create_for_tables_face (void)
{
hb_face_t *face_abc = hb_test_open_font_file ("fonts/Roboto-Regular.abc.ttf");
hb_face_t *face_ac = hb_test_open_font_file ("fonts/Roboto-Regular.ac.ttf");
hb_face_t *face_create_for_tables = hb_face_create_for_tables (
_ref_table,
face_abc,
NULL);
hb_set_t *codepoints = hb_set_create();
hb_set_add (codepoints, 97);
hb_set_add (codepoints, 99);
hb_subset_input_t* input = hb_subset_test_create_input (codepoints);
hb_set_destroy (codepoints);
hb_face_t* face_abc_subset = hb_subset_or_fail (face_create_for_tables, input);
hb_subset_test_check (face_ac, face_abc_subset, HB_TAG ('l','o','c', 'a'));
hb_subset_test_check (face_ac, face_abc_subset, HB_TAG ('g','l','y','f'));
hb_subset_test_check (face_ac, face_abc_subset, HB_TAG ('g','a','s','p'));
hb_subset_input_destroy (input);
hb_face_destroy (face_abc_subset);
hb_face_destroy (face_create_for_tables);
hb_face_destroy (face_abc);
hb_face_destroy (face_ac);
}
int
main (int argc, char **argv)
{
@ -204,6 +240,7 @@ main (int argc, char **argv)
hb_test_add (test_subset_set_flags);
hb_test_add (test_subset_sets);
hb_test_add (test_subset_plan);
hb_test_add (test_subset_create_for_tables_face);
return hb_test_run();
}