From 9564d987390a8437e2d077432629899a7bcd6d0f Mon Sep 17 00:00:00 2001 From: Garret Rieger Date: Wed, 25 May 2022 21:16:37 +0000 Subject: [PATCH] [subset] fix subsetting of faces created via hb_face_create_for_tables. Fixes #3609. --- src/hb-subset.cc | 83 +++++++++++++++++++++++++++++++++++++++++- test/api/test-subset.c | 36 ++++++++++++++++++ 2 files changed, 117 insertions(+), 2 deletions(-) diff --git a/src/hb-subset.cc b/src/hb-subset.cc index 31ddb4f89..bb38966e9 100644 --- a/src/hb-subset.cc +++ b/src/hb-subset.cc @@ -78,6 +78,85 @@ using OT::Layout::GSUB::GSUB; * retain glyph ids option and configure the subset to pass through the layout tables untouched. */ +/* + * The list of tables that the subsetter can perform subsetting for. + */ +static hb_tag_t handled_tables[] { + HB_OT_TAG_glyf, + HB_OT_TAG_hdmx, + HB_OT_TAG_name, + HB_OT_TAG_head, + HB_OT_TAG_hhea, + HB_OT_TAG_hmtx, + HB_OT_TAG_vhea, + HB_OT_TAG_vmtx, + HB_OT_TAG_maxp, + HB_OT_TAG_sbix, + HB_OT_TAG_loca, + HB_OT_TAG_cmap, + HB_OT_TAG_OS2, + HB_OT_TAG_post, + HB_OT_TAG_COLR, + HB_OT_TAG_CPAL, + HB_OT_TAG_CBLC, + HB_OT_TAG_CBDT, + HB_OT_TAG_MATH, + HB_OT_TAG_cff1, + HB_OT_TAG_cff2, + HB_OT_TAG_VORG, + HB_OT_TAG_GDEF, + HB_OT_TAG_GSUB, + HB_OT_TAG_GPOS, + HB_OT_TAG_gvar, + HB_OT_TAG_HVAR, + HB_OT_TAG_VVAR +}; + +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_face_t *face, + unsigned int start_offset, + unsigned int *table_count, /* IN/OUT */ + hb_tag_t *table_tags /* OUT */) +{ + unsigned num_tables = hb_face_get_table_tags (face, 0, nullptr, nullptr); + if (num_tables) + return hb_face_get_table_tags (face, start_offset, table_count, table_tags); + + // If face has 0 tables associated with it, assume that it has 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_array_t (handled_tables); + + while (bool (it) && start_offset > 0) + { + if (!_table_is_empty (face, *it)) + start_offset--; + + it++; + } + + unsigned num_written = 0; + while (bool (it) && num_written < *table_count) + { + if (!_table_is_empty (face, *it)) + table_tags[num_written++] = *it; + + it++; + } + + *table_count = num_written; + return 0; +} + + static unsigned _plan_estimate_subset_table_size (hb_subset_plan_t *plan, unsigned table_len, @@ -226,7 +305,7 @@ _is_table_present (hb_face_t *source, hb_tag_t 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)) + while ((_get_table_tags (source, offset, &num_tables, table_tags), num_tables)) { for (unsigned i = 0; i < num_tables; ++i) if (table_tags[i] == tag) @@ -388,7 +467,7 @@ hb_subset_plan_execute_or_fail (hb_subset_plan_t *plan) unsigned offset = 0, num_tables = ARRAY_LENGTH (table_tags); hb_vector_t 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->source, offset, &num_tables, table_tags), num_tables)) { for (unsigned i = 0; i < num_tables; ++i) { diff --git a/test/api/test-subset.c b/test/api/test-subset.c index d6ee3676c..660799705 100644 --- a/test/api/test-subset.c +++ b/test/api/test-subset.c @@ -193,6 +193,41 @@ 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_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 +239,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(); }