From 2f941053111d60433ab39cc70edd69c962896961 Mon Sep 17 00:00:00 2001 From: Garret Rieger Date: Thu, 8 Feb 2018 15:55:12 -0800 Subject: [PATCH 01/17] Disable subset tests on cmake for now. --- test/subset/CMakeLists.txt | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/test/subset/CMakeLists.txt b/test/subset/CMakeLists.txt index 0a1e8f953..6fe377e14 100644 --- a/test/subset/CMakeLists.txt +++ b/test/subset/CMakeLists.txt @@ -2,8 +2,9 @@ if (HB_BUILD_UTILS) file (READ "${CMAKE_CURRENT_SOURCE_DIR}/data/Makefile.sources" SOURCES) extract_make_variable (TESTS ${SOURCES}) foreach (test IN ITEMS ${TESTS}) - add_test (NAME ${test} - COMMAND python run-tests.py $ "data/${test}" - WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) +# TODO(grieger): Re-enable once ttx is available in CI environments. +# add_test (NAME ${test} +# COMMAND python run-tests.py $ "data/${test}" +# WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) endforeach () endif () From aac7d962120aa137385324b33a173df4f19fd80b Mon Sep 17 00:00:00 2001 From: Garret Rieger Date: Thu, 8 Feb 2018 18:18:16 -0800 Subject: [PATCH 02/17] Apply per table subsetting while building the new face in hb_subset. --- src/hb-subset-glyf.cc | 1 + src/hb-subset.cc | 105 +++++++++++++++++++++++++----------------- 2 files changed, 65 insertions(+), 41 deletions(-) diff --git a/src/hb-subset-glyf.cc b/src/hb-subset-glyf.cc index 8221a43d7..d7edd750a 100644 --- a/src/hb-subset-glyf.cc +++ b/src/hb-subset-glyf.cc @@ -101,6 +101,7 @@ _hb_subset_glyf_and_loca (const OT::glyf::accelerator_t &glyf, // TODO(grieger): Subset loca simultaneously. // TODO(grieger): Don't fail on bad offsets, just dump them. // TODO(grieger): Support short loca output. + // TODO(grieger): Add a extra loca entry at the end. unsigned int glyf_prime_size; unsigned int loca_prime_size; diff --git a/src/hb-subset.cc b/src/hb-subset.cc index a46cbd08f..f7c215bc6 100644 --- a/src/hb-subset.cc +++ b/src/hb-subset.cc @@ -253,6 +253,52 @@ hb_subset_face_add_table (hb_face_t *face, hb_tag_t tag, hb_blob_t *blob) return true; } +bool +_subset_glyf (hb_subset_plan_t *plan, hb_face_t *source, hb_face_t *dest) +{ + hb_blob_t *glyf_prime = nullptr; + hb_blob_t *loca_prime = nullptr; + + bool success = true; + // TODO(grieger): Migrate to subset function on the table like cmap. + if (hb_subset_glyf_and_loca (plan, source, &glyf_prime, &loca_prime)) { + hb_subset_face_add_table (dest, HB_OT_TAG_glyf, glyf_prime); + hb_subset_face_add_table (dest, HB_OT_TAG_loca, loca_prime); + } else { + success = false; + } + hb_blob_destroy (loca_prime); + hb_blob_destroy (glyf_prime); + + return success; +} + +bool +_subset_table (hb_subset_plan_t *plan, + hb_face_t *source, + hb_tag_t tag, + hb_blob_t *table_blob, + hb_face_t *dest) +{ + // TODO (grieger): Handle updating the head table (loca format + num glyphs) + switch (tag) { + case HB_OT_TAG_glyf: + return _subset_glyf (plan, source, dest); + case HB_OT_TAG_loca: + // SKIP loca, it's handle by the glyf subsetter. + return true; + case HB_OT_TAG_cmap: + // TODO(rsheeter): remove hb_subset_face_add_table + // once cmap subsetting works. + hb_subset_face_add_table (dest, tag, table_blob); + return subset (plan, source, dest); + default: + // Default action, copy table as is. + hb_subset_face_add_table (dest, tag, table_blob); + return true; + } +} + /** * hb_subset: * @source: font face data to be subset. @@ -270,25 +316,6 @@ hb_subset (hb_face_t *source, hb_subset_plan_t *plan = hb_subset_plan_create (source, profile, input); - hb_face_t *face = hb_subset_face_create (); - - /* Copy tables to new face. */ - { - hb_tag_t table_tags[32]; - unsigned int offset = 0, count; - do { - count = ARRAY_LENGTH (table_tags); - hb_face_get_table_tags (source, offset, &count, table_tags); - for (unsigned int i = 0; i < count; i++) - { - hb_tag_t tag = table_tags[i]; - hb_blob_t *blob = hb_face_reference_table (source, tag); - hb_subset_face_add_table (face, tag, blob); - hb_blob_destroy (blob); - } - } while (count == ARRAY_LENGTH (table_tags)); - } - hb_codepoint_t old_gid = -1; while (hb_set_next (plan->glyphs_to_retain, &old_gid)) { hb_codepoint_t new_gid; @@ -298,29 +325,25 @@ hb_subset (hb_face_t *source, DEBUG_MSG (SUBSET, nullptr, "Remap %d : DOOM! No new ID", old_gid); } } - // TODO: - // - Create initial header + table directory - // - Loop through the set of tables to be kept: - // - Perform table specific subsetting if defined. - // - copy the table into the output. - // - Fix header + table directory. + hb_face_t *dest = hb_subset_face_create (); + hb_tag_t table_tags[32]; + unsigned int offset = 0, count; bool success = true; + do { + count = ARRAY_LENGTH (table_tags); + hb_face_get_table_tags (source, offset, &count, table_tags); + for (unsigned int i = 0; i < count; i++) + { + hb_tag_t tag = table_tags[i]; + hb_blob_t *blob = hb_face_reference_table (source, tag); + success = success && _subset_table (plan, source, tag, blob, dest); + hb_blob_destroy (blob); + } + } while (count == ARRAY_LENGTH (table_tags)); - hb_face_t *dest = nullptr; // TODO allocate dest - - hb_blob_t *glyf_prime = nullptr; - hb_blob_t *loca_prime = nullptr; - if (hb_subset_glyf_and_loca (plan, source, &glyf_prime, &loca_prime)) { - // TODO: write new glyf and loca to new face. - } else { - success = false; - } - hb_blob_destroy (glyf_prime); - - success = success && subset(plan, source, dest); - - hb_subset_plan_destroy (plan); - - return face; + // TODO(grieger): Remove once basic subsetting is working + tests updated. + hb_face_destroy (dest); + hb_face_reference (source); + return success ? source : nullptr; } From 5a34114f9685680d4a8cdf85a8ac90172c5620d7 Mon Sep 17 00:00:00 2001 From: Garret Rieger Date: Thu, 8 Feb 2018 18:32:24 -0800 Subject: [PATCH 03/17] Add an extra entry to the end of the loca table to identify the end of the last glyph's data. --- src/hb-subset-glyf.cc | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/hb-subset-glyf.cc b/src/hb-subset-glyf.cc index d7edd750a..49c52a916 100644 --- a/src/hb-subset-glyf.cc +++ b/src/hb-subset-glyf.cc @@ -72,8 +72,9 @@ _write_glyf_and_loca_prime (const OT::glyf::accelerator_t &glyf, hb_codepoint_t next_glyph = -1; hb_codepoint_t new_glyph_id = 0; + unsigned int end_offset; while (hb_set_next(glyph_ids, &next_glyph)) { - unsigned int start_offset, end_offset; + unsigned int start_offset; if (unlikely (!glyf.get_offsets (next_glyph, &start_offset, &end_offset))) { return false; } @@ -86,6 +87,10 @@ _write_glyf_and_loca_prime (const OT::glyf::accelerator_t &glyf, new_glyph_id++; } + // Add the last loca entry which doesn't correspond to a specific glyph + // but identifies the end of the last glyphs data. + loca_prime[new_glyph_id].set(end_offset); + return true; } From a19138e668e77a0c05da2ab065c5366c8359b377 Mon Sep 17 00:00:00 2001 From: Rod Sheeter Date: Thu, 8 Feb 2018 19:03:41 -0800 Subject: [PATCH 04/17] comment the serialization of table --- src/hb-open-file-private.hh | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/hb-open-file-private.hh b/src/hb-open-file-private.hh index ab168ab87..d0e03ddcb 100644 --- a/src/hb-open-file-private.hh +++ b/src/hb-open-file-private.hh @@ -133,10 +133,15 @@ typedef struct OffsetTable unsigned int table_count) { TRACE_SERIALIZE (this); + // alloc 12 for the OTHeader if (unlikely (!c->extend_min (*this))) return_trace (false); + // write sfntVersion (bytes 0..3) sfnt_version.set (sfnt_tag); + // take space for numTables, searchRange, entrySelector, RangeShift + // and the TableRecords themselves if (unlikely (!tables.serialize (c, table_count))) return_trace (false); + // write OffsetTables, alloc for and write actual table blobs for (unsigned int i = 0; i < table_count; i++) { TableRecord &rec = tables.array[i]; @@ -145,9 +150,12 @@ typedef struct OffsetTable rec.length.set (hb_blob_get_length (blob)); rec.checkSum.set_for_data (hb_blob_get_data (blob, nullptr), rec.length); rec.offset.serialize (c, this); + // take room for the tablerec void *p = c->allocate_size (rec.length); if (unlikely (!p)) {return false;} + // copy the actual table memcpy (p, hb_blob_get_data (blob, nullptr), rec.length); + // 4-byte allignment if (rec.length % 4) p = c->allocate_size (4 - rec.length % 4); } From 5cca0c07afbe9ab4b28d333f6f853063ecd75aff Mon Sep 17 00:00:00 2001 From: Rod Sheeter Date: Thu, 8 Feb 2018 19:05:46 -0800 Subject: [PATCH 05/17] fix comment --- src/hb-open-file-private.hh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hb-open-file-private.hh b/src/hb-open-file-private.hh index d0e03ddcb..e425afc3f 100644 --- a/src/hb-open-file-private.hh +++ b/src/hb-open-file-private.hh @@ -150,7 +150,7 @@ typedef struct OffsetTable rec.length.set (hb_blob_get_length (blob)); rec.checkSum.set_for_data (hb_blob_get_data (blob, nullptr), rec.length); rec.offset.serialize (c, this); - // take room for the tablerec + // take room for the table void *p = c->allocate_size (rec.length); if (unlikely (!p)) {return false;} // copy the actual table From 8431c38cdc05ddcddb1aa5fbb72a95446b500fd2 Mon Sep 17 00:00:00 2001 From: Rod Sheeter Date: Thu, 8 Feb 2018 19:20:58 -0800 Subject: [PATCH 06/17] remove output noise --- util/hb-subset.cc | 1 - 1 file changed, 1 deletion(-) diff --git a/util/hb-subset.cc b/util/hb-subset.cc index 37ec7b51d..db1ca115c 100644 --- a/util/hb-subset.cc +++ b/util/hb-subset.cc @@ -59,7 +59,6 @@ struct subset_consumer_t gunichar cp = g_utf8_get_char(c); hb_codepoint_t hb_cp = cp; // TODO(Q1) is this safe? hb_set_add(codepoints, hb_cp); - g_print (" U+%04X %" G_GINT32_FORMAT "\n", cp, cp); } while ((c = g_utf8_find_next_char(c, text + text_len)) != nullptr); } From 59c658c8d53481990fe0efa66422353d0687474b Mon Sep 17 00:00:00 2001 From: Rod Sheeter Date: Thu, 8 Feb 2018 19:22:47 -0800 Subject: [PATCH 07/17] capture codepoints sorted so we can use them for cmap later. one day we will have a map --- src/hb-ot-cmap-table.hh | 14 -------- src/hb-private.hh | 65 +++++++++++++++++++++-------------- src/hb-subset-glyf.cc | 16 ++++----- src/hb-subset-plan.cc | 75 ++++++++++++++++++++++++++++------------- src/hb-subset-plan.hh | 8 +++-- src/hb-subset.cc | 29 ++++++++++------ 6 files changed, 125 insertions(+), 82 deletions(-) diff --git a/src/hb-ot-cmap-table.hh b/src/hb-ot-cmap-table.hh index 537336806..9d4f0eec6 100644 --- a/src/hb-ot-cmap-table.hh +++ b/src/hb-ot-cmap-table.hh @@ -506,20 +506,6 @@ struct cmap inline bool subset (hb_subset_plan_t *plan, hb_face_t *source, hb_face_t *dest) const { - // TODO something useful re: memory, write to dest - size_t dest_sz = 64536; // as much as anyone would ever need - void *dest_buf = malloc(dest_sz); - OT::hb_serialize_context_t context(dest_buf, dest_sz); - - // Same version - OT::cmap new_cmap; - new_cmap.version = version; - new_cmap.encodingRecord.len.set(1); // one format 12 subtable - - // TODO we need to actually build the format 12 subtable - - // TODO: this fails - // out->extend_min(new_cmap); return true; } diff --git a/src/hb-private.hh b/src/hb-private.hh index 59d732afc..a3d1250a6 100644 --- a/src/hb-private.hh +++ b/src/hb-private.hh @@ -418,34 +418,44 @@ struct hb_prealloced_array_t return &array[len - 1]; } + // Alloc enouch for size if size < allocated. Don't adjust len. + inline bool alloc(unsigned int size) + { + if (likely (size <= allocated)) + { + return true; + } + /* Need to reallocate */ + + unsigned int new_allocated = allocated; + while (size >= new_allocated) + new_allocated += (new_allocated >> 1) + 8; + + Type *new_array = nullptr; + + if (array == static_array) { + new_array = (Type *) calloc (new_allocated, sizeof (Type)); + if (new_array) + memcpy (new_array, array, len * sizeof (Type)); + } else { + bool overflows = (new_allocated < allocated) || _hb_unsigned_int_mul_overflows (new_allocated, sizeof (Type)); + if (likely (!overflows)) { + new_array = (Type *) realloc (array, new_allocated * sizeof (Type)); + } + } + + if (unlikely (!new_array)) + return false; + + array = new_array; + allocated = new_allocated; + } + inline bool resize (unsigned int size) { - if (unlikely (size > allocated)) + if (!alloc(size)) { - /* Need to reallocate */ - - unsigned int new_allocated = allocated; - while (size >= new_allocated) - new_allocated += (new_allocated >> 1) + 8; - - Type *new_array = nullptr; - - if (array == static_array) { - new_array = (Type *) calloc (new_allocated, sizeof (Type)); - if (new_array) - memcpy (new_array, array, len * sizeof (Type)); - } else { - bool overflows = (new_allocated < allocated) || _hb_unsigned_int_mul_overflows (new_allocated, sizeof (Type)); - if (likely (!overflows)) { - new_array = (Type *) realloc (array, new_allocated * sizeof (Type)); - } - } - - if (unlikely (!new_array)) - return false; - - array = new_array; - allocated = new_allocated; + return false; } len = size; @@ -488,6 +498,11 @@ struct hb_prealloced_array_t return nullptr; } + inline void qsort (int (*cmp)(const void*, const void*)) + { + ::qsort (array, len, sizeof (Type), cmp); + } + inline void qsort (void) { ::qsort (array, len, sizeof (Type), Type::cmp); diff --git a/src/hb-subset-glyf.cc b/src/hb-subset-glyf.cc index 49c52a916..b7412fbb3 100644 --- a/src/hb-subset-glyf.cc +++ b/src/hb-subset-glyf.cc @@ -31,14 +31,14 @@ bool _calculate_glyf_and_loca_prime_size (const OT::glyf::accelerator_t &glyf, - hb_set_t *glyph_ids, + hb_auto_array_t &glyph_ids, unsigned int *glyf_size /* OUT */, unsigned int *loca_size /* OUT */) { unsigned int total = 0; unsigned int count = 0; - hb_codepoint_t next_glyph = -1; - while (hb_set_next(glyph_ids, &next_glyph)) { + for (unsigned int i = 0; i < glyph_ids.len; i++) { + hb_codepoint_t next_glyph = glyph_ids[i]; unsigned int start_offset, end_offset; if (unlikely (!glyf.get_offsets (next_glyph, &start_offset, &end_offset))) { *glyf_size = 0; @@ -58,7 +58,7 @@ _calculate_glyf_and_loca_prime_size (const OT::glyf::accelerator_t &glyf, bool _write_glyf_and_loca_prime (const OT::glyf::accelerator_t &glyf, const char *glyf_data, - const hb_set_t *glyph_ids, + hb_auto_array_t &glyph_ids, int glyf_prime_size, char *glyf_prime_data /* OUT */, int loca_prime_size, @@ -73,9 +73,9 @@ _write_glyf_and_loca_prime (const OT::glyf::accelerator_t &glyf, hb_codepoint_t new_glyph_id = 0; unsigned int end_offset; - while (hb_set_next(glyph_ids, &next_glyph)) { + for (unsigned int i = 0; i < glyph_ids.len; i++) { unsigned int start_offset; - if (unlikely (!glyf.get_offsets (next_glyph, &start_offset, &end_offset))) { + if (unlikely (!glyf.get_offsets (glyph_ids[i], &start_offset, &end_offset))) { return false; } @@ -97,7 +97,7 @@ _write_glyf_and_loca_prime (const OT::glyf::accelerator_t &glyf, bool _hb_subset_glyf_and_loca (const OT::glyf::accelerator_t &glyf, const char *glyf_data, - hb_set_t *glyphs_to_retain, + hb_auto_array_t &glyphs_to_retain, hb_blob_t **glyf_prime /* OUT */, hb_blob_t **loca_prime /* OUT */) { @@ -158,7 +158,7 @@ hb_subset_glyf_and_loca (hb_subset_plan_t *plan, OT::glyf::accelerator_t glyf; glyf.init(face); - bool result = _hb_subset_glyf_and_loca (glyf, glyf_data, plan->glyphs_to_retain, glyf_prime, loca_prime); + bool result = _hb_subset_glyf_and_loca (glyf, glyf_data, plan->gids_to_retain, glyf_prime, loca_prime); glyf.fini(); // TODO(grieger): Subset loca diff --git a/src/hb-subset-plan.cc b/src/hb-subset-plan.cc index 6f889b3c5..9dbc5a095 100644 --- a/src/hb-subset-plan.cc +++ b/src/hb-subset-plan.cc @@ -29,47 +29,75 @@ #include "hb-subset-plan.hh" #include "hb-ot-cmap-table.hh" +int hb_codepoint_t_cmp(const void *l, const void *r) { + return *((hb_codepoint_t *) l) - *((hb_codepoint_t *) r); +} + hb_bool_t hb_subset_plan_new_gid_for_old_id(hb_subset_plan_t *plan, hb_codepoint_t old_gid, hb_codepoint_t *new_gid) { - // TODO(Q1) lookup in map from old:new gid - // TEMPORARY: just loop over ids to retain and count up - hb_codepoint_t current = -1; - hb_codepoint_t count = 0; - while (hb_set_next(plan->glyphs_to_retain, ¤t)) { - if (old_gid == current) { - *new_gid = count; + + // the index in old_gids is the new gid; only up to codepoints.len are valid + for (unsigned int i = 0; i < plan->codepoints.len; i++) { + if (plan->gids_to_retain[i] == old_gid) { + *new_gid = i; return true; } - count++; } return false; } -hb_set_t * -glyph_ids_to_retain (hb_face_t *face, - hb_set_t *codepoints) +void populate_codepoints(hb_set_t *input_codepoints, + hb_auto_array_t& plan_codepoints) { + plan_codepoints.alloc(hb_set_get_population(input_codepoints)); + hb_codepoint_t cp = -1; + while (hb_set_next(input_codepoints, &cp)) { + hb_codepoint_t *wr = plan_codepoints.push(); + *wr = cp; + } + plan_codepoints.qsort(hb_codepoint_t_cmp); +} + +void +populate_gids_to_retain (hb_face_t *face, + hb_auto_array_t& codepoints, + hb_auto_array_t& old_gids) { OT::cmap::accelerator_t cmap; cmap.init (face); - hb_codepoint_t cp = -1; - hb_set_t *gids = hb_set_create(); - while (hb_set_next(codepoints, &cp)) { + + hb_auto_array_t bad_indices; + + old_gids.alloc(codepoints.len); + for (unsigned int i = 0; i < codepoints.len; i++) { hb_codepoint_t gid; - if (cmap.get_nominal_glyph(cp, &gid)) { - DEBUG_MSG(SUBSET, nullptr, "gid for U+%04X is %d", cp, gid); - hb_set_add(gids, gid); - } else { - DEBUG_MSG(SUBSET, nullptr, "Unable to resolve gid for U+%04X", cp); + if (!cmap.get_nominal_glyph(codepoints[i], &gid)) { + gid = -1; + *(bad_indices.push()) = i; } + *(old_gids.push()) = gid; } + while (bad_indices.len > 0) { + unsigned int i = bad_indices[bad_indices.len - 1]; + bad_indices.pop(); + DEBUG_MSG(SUBSET, nullptr, "Drop U+%04X; no gid", codepoints[i]); + codepoints.remove(i); + old_gids.remove(i); + } + + for (unsigned int i = 0; i < codepoints.len; i++) { + DEBUG_MSG(SUBSET, nullptr, " U+%04X, old_gid %d, new_gid %d", codepoints[i], old_gids[i], i); + } + + // TODO always keep .notdef + + // TODO(Q1) expand with glyphs that make up complex glyphs // TODO expand with glyphs reached by G* // cmap.fini (); - return gids; } /** @@ -88,7 +116,8 @@ hb_subset_plan_create (hb_face_t *face, hb_subset_input_t *input) { hb_subset_plan_t *plan = hb_object_create (); - plan->glyphs_to_retain = glyph_ids_to_retain (face, input->codepoints); + populate_codepoints(input->codepoints, plan->codepoints); + populate_gids_to_retain(face, plan->codepoints, plan->gids_to_retain); return plan; } @@ -96,7 +125,6 @@ hb_subset_plan_t * hb_subset_plan_get_empty () { hb_subset_plan_t *plan = hb_object_create (); - plan->glyphs_to_retain = hb_set_get_empty(); return plan; } @@ -110,6 +138,7 @@ hb_subset_plan_destroy (hb_subset_plan_t *plan) { if (!hb_object_destroy (plan)) return; - hb_set_destroy (plan->glyphs_to_retain); + plan->codepoints.finish(); + plan->gids_to_retain.finish(); free (plan); } diff --git a/src/hb-subset-plan.hh b/src/hb-subset-plan.hh index a1e4e9e9c..c7e9108c9 100644 --- a/src/hb-subset-plan.hh +++ b/src/hb-subset-plan.hh @@ -21,7 +21,7 @@ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. * - * Google Author(s): Garret Rieger + * Google Author(s): Garret Rieger, Roderick Sheeter */ #ifndef HB_SUBSET_PLAN_HH @@ -35,7 +35,11 @@ struct hb_subset_plan_t { hb_object_header_t header; ASSERT_POD (); - hb_set_t *glyphs_to_retain; + // TODO(Q1) actual map, drop this crap + // Look at me ma, I'm a poor mans map codepoint : new gid + // codepoints is sorted and aligned with gids_to_retain. + hb_auto_array_t codepoints; + hb_auto_array_t gids_to_retain; }; typedef struct hb_subset_plan_t hb_subset_plan_t; diff --git a/src/hb-subset.cc b/src/hb-subset.cc index f7c215bc6..50bcac794 100644 --- a/src/hb-subset.cc +++ b/src/hb-subset.cc @@ -122,8 +122,8 @@ subset (hb_subset_plan_t *plan, hb_face_t *source, hb_face_t *dest) hb_blob_destroy (table_blob); - // TODO string not numeric tag - DEBUG_MSG(SUBSET, nullptr, "Subset %d %s", TableType::tableTag, result ? "success" : "FAILED!"); + hb_tag_t tag = TableType::tableTag; + DEBUG_MSG(SUBSET, nullptr, "Subset %c%c%c%c %s", HB_UNTAG(tag), result ? "success" : "FAILED!"); return result; } @@ -316,14 +316,23 @@ hb_subset (hb_face_t *source, hb_subset_plan_t *plan = hb_subset_plan_create (source, profile, input); - hb_codepoint_t old_gid = -1; - while (hb_set_next (plan->glyphs_to_retain, &old_gid)) { - hb_codepoint_t new_gid; - if (hb_subset_plan_new_gid_for_old_id (plan, old_gid, &new_gid)) { - DEBUG_MSG (SUBSET, nullptr, "Remap %d : %d", old_gid, new_gid); - } else { - DEBUG_MSG (SUBSET, nullptr, "Remap %d : DOOM! No new ID", old_gid); - } + hb_face_t *face = hb_subset_face_create (); + + /* Copy tables to new face. */ + { + hb_tag_t table_tags[32]; + unsigned int offset = 0, count; + do { + count = ARRAY_LENGTH (table_tags); + hb_face_get_table_tags (source, offset, &count, table_tags); + for (unsigned int i = 0; i < count; i++) + { + hb_tag_t tag = table_tags[i]; + hb_blob_t *blob = hb_face_reference_table (source, tag); + hb_subset_face_add_table (face, tag, blob); + hb_blob_destroy (blob); + } + } while (count == ARRAY_LENGTH (table_tags)); } hb_face_t *dest = hb_subset_face_create (); From 1cd98d05e07498653ba60a68b6342d1a90429eba Mon Sep 17 00:00:00 2001 From: Rod Sheeter Date: Thu, 8 Feb 2018 19:39:57 -0800 Subject: [PATCH 08/17] Create the groups for a cmap format12. Does not yet build the actual table. --- src/hb-ot-cmap-table.hh | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/hb-ot-cmap-table.hh b/src/hb-ot-cmap-table.hh index 9d4f0eec6..bb3eba47f 100644 --- a/src/hb-ot-cmap-table.hh +++ b/src/hb-ot-cmap-table.hh @@ -193,6 +193,7 @@ struct CmapSubtableLongGroup { friend struct CmapSubtableFormat12; friend struct CmapSubtableFormat13; + friend struct cmap; int cmp (hb_codepoint_t codepoint) const { @@ -506,6 +507,30 @@ struct cmap inline bool subset (hb_subset_plan_t *plan, hb_face_t *source, hb_face_t *dest) const { + hb_auto_array_t groups; + CmapSubtableLongGroup *group = nullptr; + for (unsigned int i = 0; i < plan->codepoints.len; i++) { + hb_codepoint_t cp = plan->codepoints[i]; + if (!group) + { + group = groups.push(); + group->startCharCode.set(cp); + group->endCharCode.set(cp); + group->glyphID.set(i); // index in codepoints is new gid + } else if (cp -1 == group->endCharCode) + { + group->endCharCode.set(cp); + } else + { + group = nullptr; + } + } + + DEBUG_MSG(SUBSET, nullptr, "cmap"); + for (unsigned int i = 0; i < groups.len; i++) { + CmapSubtableLongGroup& group = groups[i]; + DEBUG_MSG(SUBSET, nullptr, " %d: U+%04X-U+%04X, first gid %d", i, (uint32_t) group.startCharCode, (uint32_t) group.endCharCode, (uint32_t) group.glyphID); + } return true; } From 335bbaa66f66e86d417cc123a2bf397e8b834f64 Mon Sep 17 00:00:00 2001 From: Garret Rieger Date: Fri, 9 Feb 2018 10:55:15 -0800 Subject: [PATCH 09/17] Remove uneeded code in hb-subset. --- src/hb-subset.cc | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/src/hb-subset.cc b/src/hb-subset.cc index 50bcac794..73812b72d 100644 --- a/src/hb-subset.cc +++ b/src/hb-subset.cc @@ -316,25 +316,6 @@ hb_subset (hb_face_t *source, hb_subset_plan_t *plan = hb_subset_plan_create (source, profile, input); - hb_face_t *face = hb_subset_face_create (); - - /* Copy tables to new face. */ - { - hb_tag_t table_tags[32]; - unsigned int offset = 0, count; - do { - count = ARRAY_LENGTH (table_tags); - hb_face_get_table_tags (source, offset, &count, table_tags); - for (unsigned int i = 0; i < count; i++) - { - hb_tag_t tag = table_tags[i]; - hb_blob_t *blob = hb_face_reference_table (source, tag); - hb_subset_face_add_table (face, tag, blob); - hb_blob_destroy (blob); - } - } while (count == ARRAY_LENGTH (table_tags)); - } - hb_face_t *dest = hb_subset_face_create (); hb_tag_t table_tags[32]; unsigned int offset = 0, count; From 1582eabee6017839518b821ef93a329a0a86a453 Mon Sep 17 00:00:00 2001 From: Garret Rieger Date: Fri, 9 Feb 2018 12:52:08 -0800 Subject: [PATCH 10/17] Update head table with loca format selected by glyf+loca subsetting. --- src/hb-subset-glyf.cc | 7 +++---- src/hb-subset-glyf.hh | 1 + src/hb-subset.cc | 37 +++++++++++++++++++++++++++++++++++-- 3 files changed, 39 insertions(+), 6 deletions(-) diff --git a/src/hb-subset-glyf.cc b/src/hb-subset-glyf.cc index b7412fbb3..c6e2b5dbd 100644 --- a/src/hb-subset-glyf.cc +++ b/src/hb-subset-glyf.cc @@ -103,10 +103,8 @@ _hb_subset_glyf_and_loca (const OT::glyf::accelerator_t &glyf, { // TODO(grieger): Sanity check writes to make sure they are in-bounds. // TODO(grieger): Sanity check allocation size for the new table. - // TODO(grieger): Subset loca simultaneously. // TODO(grieger): Don't fail on bad offsets, just dump them. // TODO(grieger): Support short loca output. - // TODO(grieger): Add a extra loca entry at the end. unsigned int glyf_prime_size; unsigned int loca_prime_size; @@ -150,7 +148,8 @@ _hb_subset_glyf_and_loca (const OT::glyf::accelerator_t &glyf, bool hb_subset_glyf_and_loca (hb_subset_plan_t *plan, hb_face_t *face, - hb_blob_t **glyf_prime /* OUT */, + bool *use_short_loca, /* OUT */ + hb_blob_t **glyf_prime, /* OUT */ hb_blob_t **loca_prime /* OUT */) { hb_blob_t *glyf_blob = OT::Sanitizer().sanitize (face->reference_table (HB_OT_TAG_glyf)); @@ -161,7 +160,7 @@ hb_subset_glyf_and_loca (hb_subset_plan_t *plan, bool result = _hb_subset_glyf_and_loca (glyf, glyf_data, plan->gids_to_retain, glyf_prime, loca_prime); glyf.fini(); - // TODO(grieger): Subset loca + *use_short_loca = false; return result; } diff --git a/src/hb-subset-glyf.hh b/src/hb-subset-glyf.hh index 035085f03..dbdd3410d 100644 --- a/src/hb-subset-glyf.hh +++ b/src/hb-subset-glyf.hh @@ -34,6 +34,7 @@ bool hb_subset_glyf_and_loca (hb_subset_plan_t *plan, hb_face_t *face, + bool *use_short_loca, /* OUT */ hb_blob_t **glyf_prime /* OUT */, hb_blob_t **loca_prime /* OUT */); diff --git a/src/hb-subset.cc b/src/hb-subset.cc index 73812b72d..cf8ac6cc1 100644 --- a/src/hb-subset.cc +++ b/src/hb-subset.cc @@ -253,6 +253,32 @@ hb_subset_face_add_table (hb_face_t *face, hb_tag_t tag, hb_blob_t *blob) return true; } +bool +_add_head_and_set_loca_version (hb_face_t *source, bool use_short_loca, hb_face_t *dest) +{ + hb_blob_t *head_blob = OT::Sanitizer().sanitize (hb_face_reference_table (source, HB_OT_TAG_head)); + const OT::head *head = OT::Sanitizer::lock_instance (head_blob); + + if (head) { + OT::head *head_prime = (OT::head *) calloc (OT::head::static_size, 1); + memcpy (head_prime, head, OT::head::static_size); + head_prime->indexToLocFormat.set (use_short_loca ? 0 : 1); + + hb_blob_t *head_prime_blob = hb_blob_create ((const char*) head_prime, + OT::head::static_size, + HB_MEMORY_MODE_WRITABLE, + head_prime, + free); + hb_subset_face_add_table (dest, HB_OT_TAG_head, head_prime_blob); + + hb_blob_destroy (head_prime_blob); + } + + hb_blob_destroy (head_blob); + + return !head; +} + bool _subset_glyf (hb_subset_plan_t *plan, hb_face_t *source, hb_face_t *dest) { @@ -260,16 +286,20 @@ _subset_glyf (hb_subset_plan_t *plan, hb_face_t *source, hb_face_t *dest) hb_blob_t *loca_prime = nullptr; bool success = true; + bool use_short_loca = false; // TODO(grieger): Migrate to subset function on the table like cmap. - if (hb_subset_glyf_and_loca (plan, source, &glyf_prime, &loca_prime)) { + if (hb_subset_glyf_and_loca (plan, source, &use_short_loca, &glyf_prime, &loca_prime)) { hb_subset_face_add_table (dest, HB_OT_TAG_glyf, glyf_prime); hb_subset_face_add_table (dest, HB_OT_TAG_loca, loca_prime); + success = success && _add_head_and_set_loca_version (source, use_short_loca, dest); } else { success = false; } hb_blob_destroy (loca_prime); hb_blob_destroy (glyf_prime); + _add_head_and_set_loca_version (source, use_short_loca, dest); + return success; } @@ -284,8 +314,11 @@ _subset_table (hb_subset_plan_t *plan, switch (tag) { case HB_OT_TAG_glyf: return _subset_glyf (plan, source, dest); + case HB_OT_TAG_head: + // SKIP head, it's handled by glyf + return true; case HB_OT_TAG_loca: - // SKIP loca, it's handle by the glyf subsetter. + // SKIP loca, it's handle by glyf return true; case HB_OT_TAG_cmap: // TODO(rsheeter): remove hb_subset_face_add_table From 86aa4b3ba7cd075f01614874dae88a771b8c54fd Mon Sep 17 00:00:00 2001 From: Garret Rieger Date: Fri, 9 Feb 2018 13:54:43 -0800 Subject: [PATCH 11/17] Return empty face on hb_subset failure instead of null. Plus some minor cleanups for _add_head_and_set_loca_version --- src/hb-subset.cc | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/hb-subset.cc b/src/hb-subset.cc index cf8ac6cc1..4eaf188c3 100644 --- a/src/hb-subset.cc +++ b/src/hb-subset.cc @@ -258,8 +258,9 @@ _add_head_and_set_loca_version (hb_face_t *source, bool use_short_loca, hb_face_ { hb_blob_t *head_blob = OT::Sanitizer().sanitize (hb_face_reference_table (source, HB_OT_TAG_head)); const OT::head *head = OT::Sanitizer::lock_instance (head_blob); + bool has_head = (head != nullptr); - if (head) { + if (has_head) { OT::head *head_prime = (OT::head *) calloc (OT::head::static_size, 1); memcpy (head_prime, head, OT::head::static_size); head_prime->indexToLocFormat.set (use_short_loca ? 0 : 1); @@ -276,7 +277,7 @@ _add_head_and_set_loca_version (hb_face_t *source, bool use_short_loca, hb_face_ hb_blob_destroy (head_blob); - return !head; + return has_head; } bool @@ -298,8 +299,6 @@ _subset_glyf (hb_subset_plan_t *plan, hb_face_t *source, hb_face_t *dest) hb_blob_destroy (loca_prime); hb_blob_destroy (glyf_prime); - _add_head_and_set_loca_version (source, use_short_loca, dest); - return success; } @@ -345,7 +344,7 @@ hb_subset (hb_face_t *source, hb_subset_profile_t *profile, hb_subset_input_t *input) { - if (unlikely (!profile || !input || !source)) return nullptr; + if (unlikely (!profile || !input || !source)) return hb_face_get_empty(); hb_subset_plan_t *plan = hb_subset_plan_create (source, profile, input); @@ -368,5 +367,5 @@ hb_subset (hb_face_t *source, // TODO(grieger): Remove once basic subsetting is working + tests updated. hb_face_destroy (dest); hb_face_reference (source); - return success ? source : nullptr; + return success ? source : hb_face_get_empty(); } From 3bc81558d836e27e77bda0d6da9c71f530719579 Mon Sep 17 00:00:00 2001 From: Garret Rieger Date: Fri, 9 Feb 2018 16:06:33 -0800 Subject: [PATCH 12/17] Formatting for hb-subset-plan. --- src/hb-subset-plan.cc | 51 +++++++++++++++++++++++-------------------- 1 file changed, 27 insertions(+), 24 deletions(-) diff --git a/src/hb-subset-plan.cc b/src/hb-subset-plan.cc index 9dbc5a095..4cb4b7c95 100644 --- a/src/hb-subset-plan.cc +++ b/src/hb-subset-plan.cc @@ -29,15 +29,16 @@ #include "hb-subset-plan.hh" #include "hb-ot-cmap-table.hh" -int hb_codepoint_t_cmp(const void *l, const void *r) { +int +hb_codepoint_t_cmp (const void *l, const void *r) { return *((hb_codepoint_t *) l) - *((hb_codepoint_t *) r); } hb_bool_t -hb_subset_plan_new_gid_for_old_id(hb_subset_plan_t *plan, - hb_codepoint_t old_gid, - hb_codepoint_t *new_gid) { - +hb_subset_plan_new_gid_for_old_id (hb_subset_plan_t *plan, + hb_codepoint_t old_gid, + hb_codepoint_t *new_gid) +{ // the index in old_gids is the new gid; only up to codepoints.len are valid for (unsigned int i = 0; i < plan->codepoints.len; i++) { if (plan->gids_to_retain[i] == old_gid) { @@ -48,43 +49,45 @@ hb_subset_plan_new_gid_for_old_id(hb_subset_plan_t *plan, return false; } -void populate_codepoints(hb_set_t *input_codepoints, - hb_auto_array_t& plan_codepoints) { - plan_codepoints.alloc(hb_set_get_population(input_codepoints)); +void +_populate_codepoints (hb_set_t *input_codepoints, + hb_auto_array_t& plan_codepoints) +{ + plan_codepoints.alloc (hb_set_get_population (input_codepoints)); hb_codepoint_t cp = -1; - while (hb_set_next(input_codepoints, &cp)) { + while (hb_set_next (input_codepoints, &cp)) { hb_codepoint_t *wr = plan_codepoints.push(); *wr = cp; } - plan_codepoints.qsort(hb_codepoint_t_cmp); + plan_codepoints.qsort (hb_codepoint_t_cmp); } void -populate_gids_to_retain (hb_face_t *face, - hb_auto_array_t& codepoints, - hb_auto_array_t& old_gids) +_populate_gids_to_retain (hb_face_t *face, + hb_auto_array_t& codepoints, + hb_auto_array_t& old_gids) { OT::cmap::accelerator_t cmap; cmap.init (face); hb_auto_array_t bad_indices; - old_gids.alloc(codepoints.len); + old_gids.alloc (codepoints.len); for (unsigned int i = 0; i < codepoints.len; i++) { hb_codepoint_t gid; - if (!cmap.get_nominal_glyph(codepoints[i], &gid)) { + if (!cmap.get_nominal_glyph (codepoints[i], &gid)) { gid = -1; - *(bad_indices.push()) = i; + *(bad_indices.push ()) = i; } - *(old_gids.push()) = gid; + *(old_gids.push ()) = gid; } while (bad_indices.len > 0) { unsigned int i = bad_indices[bad_indices.len - 1]; - bad_indices.pop(); + bad_indices.pop (); DEBUG_MSG(SUBSET, nullptr, "Drop U+%04X; no gid", codepoints[i]); - codepoints.remove(i); - old_gids.remove(i); + codepoints.remove (i); + old_gids.remove (i); } for (unsigned int i = 0; i < codepoints.len; i++) { @@ -116,8 +119,8 @@ hb_subset_plan_create (hb_face_t *face, hb_subset_input_t *input) { hb_subset_plan_t *plan = hb_object_create (); - populate_codepoints(input->codepoints, plan->codepoints); - populate_gids_to_retain(face, plan->codepoints, plan->gids_to_retain); + _populate_codepoints (input->codepoints, plan->codepoints); + _populate_gids_to_retain (face, plan->codepoints, plan->gids_to_retain); return plan; } @@ -138,7 +141,7 @@ hb_subset_plan_destroy (hb_subset_plan_t *plan) { if (!hb_object_destroy (plan)) return; - plan->codepoints.finish(); - plan->gids_to_retain.finish(); + plan->codepoints.finish (); + plan->gids_to_retain.finish (); free (plan); } From 0089443756cdcef0182e55cf8480b96a64d31cc7 Mon Sep 17 00:00:00 2001 From: Garret Rieger Date: Fri, 9 Feb 2018 16:22:09 -0800 Subject: [PATCH 13/17] Keep a second set of glyph ids in subset plan which is sorted by glyph id and always has gid 0 --- src/hb-subset-glyf.cc | 2 +- src/hb-subset-plan.cc | 34 +++++++++++++++++++++++++--------- src/hb-subset-plan.hh | 1 + 3 files changed, 27 insertions(+), 10 deletions(-) diff --git a/src/hb-subset-glyf.cc b/src/hb-subset-glyf.cc index c6e2b5dbd..a186cdf7b 100644 --- a/src/hb-subset-glyf.cc +++ b/src/hb-subset-glyf.cc @@ -157,7 +157,7 @@ hb_subset_glyf_and_loca (hb_subset_plan_t *plan, OT::glyf::accelerator_t glyf; glyf.init(face); - bool result = _hb_subset_glyf_and_loca (glyf, glyf_data, plan->gids_to_retain, glyf_prime, loca_prime); + bool result = _hb_subset_glyf_and_loca (glyf, glyf_data, plan->gids_to_retain_sorted, glyf_prime, loca_prime); glyf.fini(); *use_short_loca = false; diff --git a/src/hb-subset-plan.cc b/src/hb-subset-plan.cc index 4cb4b7c95..fb8913e87 100644 --- a/src/hb-subset-plan.cc +++ b/src/hb-subset-plan.cc @@ -30,7 +30,7 @@ #include "hb-ot-cmap-table.hh" int -hb_codepoint_t_cmp (const void *l, const void *r) { +_hb_codepoint_t_cmp (const void *l, const void *r) { return *((hb_codepoint_t *) l) - *((hb_codepoint_t *) r); } @@ -40,8 +40,8 @@ hb_subset_plan_new_gid_for_old_id (hb_subset_plan_t *plan, hb_codepoint_t *new_gid) { // the index in old_gids is the new gid; only up to codepoints.len are valid - for (unsigned int i = 0; i < plan->codepoints.len; i++) { - if (plan->gids_to_retain[i] == old_gid) { + for (unsigned int i = 0; i < plan->gids_to_retain_sorted.len; i++) { + if (plan->gids_to_retain_sorted[i] == old_gid) { *new_gid = i; return true; } @@ -59,13 +59,14 @@ _populate_codepoints (hb_set_t *input_codepoints, hb_codepoint_t *wr = plan_codepoints.push(); *wr = cp; } - plan_codepoints.qsort (hb_codepoint_t_cmp); + plan_codepoints.qsort (_hb_codepoint_t_cmp); } void _populate_gids_to_retain (hb_face_t *face, hb_auto_array_t& codepoints, - hb_auto_array_t& old_gids) + hb_auto_array_t& old_gids, + hb_auto_array_t& old_gids_sorted) { OT::cmap::accelerator_t cmap; cmap.init (face); @@ -73,12 +74,16 @@ _populate_gids_to_retain (hb_face_t *face, hb_auto_array_t bad_indices; old_gids.alloc (codepoints.len); + bool has_zero = false; for (unsigned int i = 0; i < codepoints.len; i++) { hb_codepoint_t gid; if (!cmap.get_nominal_glyph (codepoints[i], &gid)) { gid = -1; *(bad_indices.push ()) = i; } + if (gid == 0) { + has_zero = true; + } *(old_gids.push ()) = gid; } @@ -90,13 +95,20 @@ _populate_gids_to_retain (hb_face_t *face, old_gids.remove (i); } + // Populate a second glyph id array that is sorted by glyph id + // and is gauranteed to contain 0. + old_gids_sorted.alloc (old_gids.len + (has_zero ? 0 : 1)); + for (unsigned int i = 0; i < old_gids.len; i++) { + *(old_gids_sorted.push ()) = old_gids[i]; + } + if (!has_zero) + *(old_gids_sorted.push ()) = 0; + old_gids_sorted.qsort (_hb_codepoint_t_cmp); + for (unsigned int i = 0; i < codepoints.len; i++) { DEBUG_MSG(SUBSET, nullptr, " U+%04X, old_gid %d, new_gid %d", codepoints[i], old_gids[i], i); } - // TODO always keep .notdef - - // TODO(Q1) expand with glyphs that make up complex glyphs // TODO expand with glyphs reached by G* // @@ -120,7 +132,10 @@ hb_subset_plan_create (hb_face_t *face, { hb_subset_plan_t *plan = hb_object_create (); _populate_codepoints (input->codepoints, plan->codepoints); - _populate_gids_to_retain (face, plan->codepoints, plan->gids_to_retain); + _populate_gids_to_retain (face, + plan->codepoints, + plan->gids_to_retain, + plan->gids_to_retain_sorted); return plan; } @@ -143,5 +158,6 @@ hb_subset_plan_destroy (hb_subset_plan_t *plan) plan->codepoints.finish (); plan->gids_to_retain.finish (); + plan->gids_to_retain_sorted.finish (); free (plan); } diff --git a/src/hb-subset-plan.hh b/src/hb-subset-plan.hh index c7e9108c9..e1c3bd3e3 100644 --- a/src/hb-subset-plan.hh +++ b/src/hb-subset-plan.hh @@ -40,6 +40,7 @@ struct hb_subset_plan_t { // codepoints is sorted and aligned with gids_to_retain. hb_auto_array_t codepoints; hb_auto_array_t gids_to_retain; + hb_auto_array_t gids_to_retain_sorted; }; typedef struct hb_subset_plan_t hb_subset_plan_t; From 4816064c0e5464d032a55001a959a9abcef7f70e Mon Sep 17 00:00:00 2001 From: Rod Sheeter Date: Fri, 9 Feb 2018 17:14:37 -0800 Subject: [PATCH 14/17] add missing return --- src/hb-private.hh | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/hb-private.hh b/src/hb-private.hh index a3d1250a6..751dec60f 100644 --- a/src/hb-private.hh +++ b/src/hb-private.hh @@ -448,7 +448,9 @@ struct hb_prealloced_array_t return false; array = new_array; - allocated = new_allocated; + allocated = new_allocated; + + return true; } inline bool resize (unsigned int size) From d2170d14780ad6f8e0d17a1e011445c3bcc20871 Mon Sep 17 00:00:00 2001 From: Garret Rieger Date: Fri, 9 Feb 2018 17:24:16 -0800 Subject: [PATCH 15/17] Check for failures from add table. --- src/hb-subset.cc | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/hb-subset.cc b/src/hb-subset.cc index 4eaf188c3..98565c307 100644 --- a/src/hb-subset.cc +++ b/src/hb-subset.cc @@ -270,7 +270,7 @@ _add_head_and_set_loca_version (hb_face_t *source, bool use_short_loca, hb_face_ HB_MEMORY_MODE_WRITABLE, head_prime, free); - hb_subset_face_add_table (dest, HB_OT_TAG_head, head_prime_blob); + has_head = has_head && hb_subset_face_add_table (dest, HB_OT_TAG_head, head_prime_blob); hb_blob_destroy (head_prime_blob); } @@ -290,8 +290,8 @@ _subset_glyf (hb_subset_plan_t *plan, hb_face_t *source, hb_face_t *dest) bool use_short_loca = false; // TODO(grieger): Migrate to subset function on the table like cmap. if (hb_subset_glyf_and_loca (plan, source, &use_short_loca, &glyf_prime, &loca_prime)) { - hb_subset_face_add_table (dest, HB_OT_TAG_glyf, glyf_prime); - hb_subset_face_add_table (dest, HB_OT_TAG_loca, loca_prime); + success = success && hb_subset_face_add_table (dest, HB_OT_TAG_glyf, glyf_prime); + success = success && hb_subset_face_add_table (dest, HB_OT_TAG_loca, loca_prime); success = success && _add_head_and_set_loca_version (source, use_short_loca, dest); } else { success = false; @@ -326,8 +326,7 @@ _subset_table (hb_subset_plan_t *plan, return subset (plan, source, dest); default: // Default action, copy table as is. - hb_subset_face_add_table (dest, tag, table_blob); - return true; + return hb_subset_face_add_table (dest, tag, table_blob); } } From 9275bd03ea427eb607dde6a8e65f78a350b88323 Mon Sep 17 00:00:00 2001 From: Rod Sheeter Date: Fri, 9 Feb 2018 17:33:34 -0800 Subject: [PATCH 16/17] First pass at building a cmap --- src/hb-ot-cmap-table.hh | 103 ++++++++++++++++++++++++++++++++++++---- src/hb-subset.cc | 37 ++++++++------- 2 files changed, 115 insertions(+), 25 deletions(-) diff --git a/src/hb-ot-cmap-table.hh b/src/hb-ot-cmap-table.hh index bb3eba47f..daee6ca5a 100644 --- a/src/hb-ot-cmap-table.hh +++ b/src/hb-ot-cmap-table.hh @@ -254,6 +254,8 @@ struct CmapSubtableFormat10 : CmapSubtableTrimmed {}; template struct CmapSubtableLongSegmented { + friend struct cmap; + inline bool get_glyph (hb_codepoint_t codepoint, hb_codepoint_t *glyph) const { int i = groups.bsearch (codepoint); @@ -269,6 +271,20 @@ struct CmapSubtableLongSegmented return_trace (c->check_struct (this) && groups.sanitize (c)); } + inline bool serialize(hb_serialize_context_t *context, + unsigned int group_count, + Supplier &group_supplier) + { + TRACE_SERIALIZE (this); + if (unlikely(!context->extend_min (*this))) return_trace (false); + if (unlikely(!groups.serialize(context, group_count))) return_trace (false); + for (unsigned int i = 0; i < group_count; i++) { + const CmapSubtableLongGroup &group = group_supplier[i]; + memcpy(&groups[i], &group, sizeof(group)); + } + return true; + } + protected: HBUINT16 format; /* Subtable format; set to 12. */ HBUINT16 reservedZ; /* Reserved; set to 0. */ @@ -505,15 +521,15 @@ struct cmap encodingRecord.sanitize (c, this)); } - inline bool subset (hb_subset_plan_t *plan, hb_face_t *source, hb_face_t *dest) const + inline void populate_groups(hb_auto_array_t &codepoints, + hb_auto_array_t *groups) const { - hb_auto_array_t groups; CmapSubtableLongGroup *group = nullptr; - for (unsigned int i = 0; i < plan->codepoints.len; i++) { - hb_codepoint_t cp = plan->codepoints[i]; + for (unsigned int i = 0; i < codepoints.len; i++) { + hb_codepoint_t cp = codepoints[i]; if (!group) { - group = groups.push(); + group = groups->push(); group->startCharCode.set(cp); group->endCharCode.set(cp); group->glyphID.set(i); // index in codepoints is new gid @@ -527,13 +543,84 @@ struct cmap } DEBUG_MSG(SUBSET, nullptr, "cmap"); - for (unsigned int i = 0; i < groups.len; i++) { - CmapSubtableLongGroup& group = groups[i]; - DEBUG_MSG(SUBSET, nullptr, " %d: U+%04X-U+%04X, first gid %d", i, (uint32_t) group.startCharCode, (uint32_t) group.endCharCode, (uint32_t) group.glyphID); + for (unsigned int i = 0; i < groups->len; i++) { + CmapSubtableLongGroup& group = (*groups)[i]; + DEBUG_MSG(SUBSET, nullptr, " %d: U+%04X-U+%04X, gid %d-%d", i, (uint32_t) group.startCharCode, (uint32_t) group.endCharCode, (uint32_t) group.glyphID, (uint32_t) group.glyphID + ((uint32_t) group.endCharCode - (uint32_t) group.startCharCode)); } + } + + hb_bool_t _subset (hb_auto_array_t &groups, + size_t dest_sz, + void *dest) const + { + hb_serialize_context_t context(dest, dest_sz); + + OT::cmap *cmap = context.start_serialize (); + if (unlikely(!context.extend_min(*cmap))) + { + return false; + } + + cmap->version.set(0); + + if (unlikely(!cmap->encodingRecord.serialize(&context, /* numTables */ 1))) + { + return false; + } + + EncodingRecord &rec = cmap->encodingRecord[0]; + rec.platformID.set (3); // Windows + rec.encodingID.set (1); // Unicode BMP + + CmapSubtable &subtable = rec.subtable.serialize(&context, &rec.subtable); + subtable.u.format.set(12); + + CmapSubtableFormat12 &format12 = subtable.u.format12; + format12.format.set(12); + format12.reservedZ.set(0); + + OT::Supplier group_supplier (&groups[0], groups.len, sizeof (CmapSubtableLongGroup)); + if (unlikely(!format12.serialize(&context, groups.len, group_supplier))) + { + return false; + } + + context.end_serialize (); return true; } + hb_blob_t * subset (hb_subset_plan_t *plan, hb_face_t *source) const + { + hb_auto_array_t groups; + + populate_groups(plan->codepoints, &groups); + + // We now know how big our blob needs to be + // TODO use APIs from the structs to get size? + size_t dest_sz = 4 // header + + 8 // 1 EncodingRecord + + 16 // Format 12 header + + 12 * groups.len; // SequentialMapGroup records + void *dest = calloc(dest_sz, 1); + if (unlikely(!dest)) { + DEBUG_MSG(SUBSET, nullptr, "Unable to alloc %ld for cmap subset output", dest_sz); + return nullptr; + } + + if (unlikely(!_subset(groups, dest_sz, dest))) + { + free(dest); + return nullptr; + } + + // all done, write the blob into dest + return hb_blob_create((const char *)dest, + dest_sz, + HB_MEMORY_MODE_READONLY, + /* userdata */ nullptr, + free); + } + struct accelerator_t { inline void init (hb_face_t *face) diff --git a/src/hb-subset.cc b/src/hb-subset.cc index 98565c307..8e1f81985 100644 --- a/src/hb-subset.cc +++ b/src/hb-subset.cc @@ -108,19 +108,19 @@ hb_subset_input_destroy(hb_subset_input_t *subset_input) } template -hb_bool_t -subset (hb_subset_plan_t *plan, hb_face_t *source, hb_face_t *dest) +hb_blob_t * +_subset (hb_subset_plan_t *plan, hb_face_t *source) { OT::Sanitizer sanitizer; - hb_blob_t *table_blob = sanitizer.sanitize (source->reference_table (TableType::tableTag)); - if (unlikely(!table_blob)) { + hb_blob_t *source_blob = sanitizer.sanitize (source->reference_table (TableType::tableTag)); + if (unlikely(!source_blob)) { DEBUG_MSG(SUBSET, nullptr, "Failed to reference table for tag %d", TableType::tableTag); - return false; + return nullptr; } - const TableType *table = OT::Sanitizer::lock_instance (table_blob); - hb_bool_t result = table->subset(plan, source, dest); + const TableType *table = OT::Sanitizer::lock_instance (source_blob); + hb_blob_t *result = table->subset(plan, source); - hb_blob_destroy (table_blob); + hb_blob_destroy (source_blob); hb_tag_t tag = TableType::tableTag; DEBUG_MSG(SUBSET, nullptr, "Subset %c%c%c%c %s", HB_UNTAG(tag), result ? "success" : "FAILED!"); @@ -242,7 +242,6 @@ hb_subset_face_add_table (hb_face_t *face, hb_tag_t tag, hb_blob_t *blob) return false; hb_subset_face_data_t *data = (hb_subset_face_data_t *) face->user_data; - hb_subset_face_data_t::table_entry_t *entry = data->tables.push (); if (unlikely (!entry)) return false; @@ -306,10 +305,12 @@ bool _subset_table (hb_subset_plan_t *plan, hb_face_t *source, hb_tag_t tag, - hb_blob_t *table_blob, + hb_blob_t *source_blob, hb_face_t *dest) { // TODO (grieger): Handle updating the head table (loca format + num glyphs) + DEBUG_MSG(SUBSET, nullptr, "begin subset %c%c%c%c", HB_UNTAG(tag)); + hb_blob_t *dest_blob; switch (tag) { case HB_OT_TAG_glyf: return _subset_glyf (plan, source, dest); @@ -320,14 +321,16 @@ _subset_table (hb_subset_plan_t *plan, // SKIP loca, it's handle by glyf return true; case HB_OT_TAG_cmap: - // TODO(rsheeter): remove hb_subset_face_add_table - // once cmap subsetting works. - hb_subset_face_add_table (dest, tag, table_blob); - return subset (plan, source, dest); + dest_blob = _subset (plan, source); + break; default: - // Default action, copy table as is. - return hb_subset_face_add_table (dest, tag, table_blob); - } + dest_blob = source_blob; + break; + } + DEBUG_MSG(SUBSET, nullptr, "subset %c%c%c%c %s", HB_UNTAG(tag), dest_blob ? "ok" : "FAILED"); + if (unlikely(!dest_blob)) return false; + if (unlikely(!hb_subset_face_add_table (dest, tag, dest_blob))) return false; + return true; } /** From d18decd2013f24f315dbd3b15cdd80c5a734e7e9 Mon Sep 17 00:00:00 2001 From: Garret Rieger Date: Fri, 9 Feb 2018 18:41:21 -0800 Subject: [PATCH 17/17] In glyf subsetting add suport for writing out a short loca table when possible. --- src/hb-subset-glyf.cc | 42 +++++++++++++++++++++++++++++++++--------- 1 file changed, 33 insertions(+), 9 deletions(-) diff --git a/src/hb-subset-glyf.cc b/src/hb-subset-glyf.cc index a186cdf7b..b0f44e2c9 100644 --- a/src/hb-subset-glyf.cc +++ b/src/hb-subset-glyf.cc @@ -32,7 +32,8 @@ bool _calculate_glyf_and_loca_prime_size (const OT::glyf::accelerator_t &glyf, hb_auto_array_t &glyph_ids, - unsigned int *glyf_size /* OUT */, + bool *use_short_loca, /* OUT */ + unsigned int *glyf_size, /* OUT */ unsigned int *loca_size /* OUT */) { unsigned int total = 0; @@ -51,23 +52,37 @@ _calculate_glyf_and_loca_prime_size (const OT::glyf::accelerator_t &glyf, } *glyf_size = total; - *loca_size = (count + 1) * sizeof(OT::HBUINT32); + *use_short_loca = (total <= 131070); + *loca_size = (count + 1) + * (*use_short_loca ? sizeof(OT::HBUINT16) : sizeof(OT::HBUINT32)); + + DEBUG_MSG(SUBSET, nullptr, "preparing to subset glyf: final size %d, loca size %d, using %s loca", + total, + *loca_size, + *use_short_loca ? "short" : "long"); return true; } +void +_write_loca_entry (unsigned int id, unsigned int offset, bool is_short, void *loca_prime) { + if (is_short) { + ((OT::HBUINT16*) loca_prime) [id].set (offset / 2); + } else { + ((OT::HBUINT32*) loca_prime) [id].set (offset); + } +} + bool _write_glyf_and_loca_prime (const OT::glyf::accelerator_t &glyf, const char *glyf_data, hb_auto_array_t &glyph_ids, + bool use_short_loca, int glyf_prime_size, char *glyf_prime_data /* OUT */, int loca_prime_size, char *loca_prime_data /* OUT */) { - // TODO(grieger): Handle the missing character glyf and outline. - char *glyf_prime_data_next = glyf_prime_data; - OT::HBUINT32 *loca_prime = (OT::HBUINT32*) loca_prime_data; hb_codepoint_t next_glyph = -1; hb_codepoint_t new_glyph_id = 0; @@ -81,7 +96,8 @@ _write_glyf_and_loca_prime (const OT::glyf::accelerator_t &glyf, int length = end_offset - start_offset; memcpy (glyf_prime_data_next, glyf_data + start_offset, length); - loca_prime[new_glyph_id].set(start_offset); + + _write_loca_entry (i, start_offset, use_short_loca, loca_prime_data); glyf_prime_data_next += length; new_glyph_id++; @@ -89,7 +105,7 @@ _write_glyf_and_loca_prime (const OT::glyf::accelerator_t &glyf, // Add the last loca entry which doesn't correspond to a specific glyph // but identifies the end of the last glyphs data. - loca_prime[new_glyph_id].set(end_offset); + _write_loca_entry (new_glyph_id, end_offset, use_short_loca, loca_prime_data); return true; } @@ -98,18 +114,20 @@ bool _hb_subset_glyf_and_loca (const OT::glyf::accelerator_t &glyf, const char *glyf_data, hb_auto_array_t &glyphs_to_retain, + bool *use_short_loca, hb_blob_t **glyf_prime /* OUT */, hb_blob_t **loca_prime /* OUT */) { // TODO(grieger): Sanity check writes to make sure they are in-bounds. // TODO(grieger): Sanity check allocation size for the new table. // TODO(grieger): Don't fail on bad offsets, just dump them. - // TODO(grieger): Support short loca output. unsigned int glyf_prime_size; unsigned int loca_prime_size; + if (unlikely (!_calculate_glyf_and_loca_prime_size (glyf, glyphs_to_retain, + use_short_loca, &glyf_prime_size, &loca_prime_size))) { return false; @@ -118,6 +136,7 @@ _hb_subset_glyf_and_loca (const OT::glyf::accelerator_t &glyf, char *glyf_prime_data = (char *) calloc (glyf_prime_size, 1); char *loca_prime_data = (char *) calloc (loca_prime_size, 1); if (unlikely (!_write_glyf_and_loca_prime (glyf, glyf_data, glyphs_to_retain, + *use_short_loca, glyf_prime_size, glyf_prime_data, loca_prime_size, loca_prime_data))) { free (glyf_prime_data); @@ -157,7 +176,12 @@ hb_subset_glyf_and_loca (hb_subset_plan_t *plan, OT::glyf::accelerator_t glyf; glyf.init(face); - bool result = _hb_subset_glyf_and_loca (glyf, glyf_data, plan->gids_to_retain_sorted, glyf_prime, loca_prime); + bool result = _hb_subset_glyf_and_loca (glyf, + glyf_data, + plan->gids_to_retain_sorted, + use_short_loca, + glyf_prime, + loca_prime); glyf.fini(); *use_short_loca = false;