From fa87770372a3156658412ff0d70e32083c6b0484 Mon Sep 17 00:00:00 2001 From: Rod Sheeter Date: Wed, 14 Feb 2018 14:16:25 -0800 Subject: [PATCH] [subset] First pass at hmtx --- src/hb-ot-cmap-table.hh | 19 +- src/hb-ot-hhea-table.hh | 6 +- src/hb-ot-hmtx-table.hh | 163 +++++++++++++++-- src/hb-ot-maxp-table.hh | 8 +- src/hb-ot-os2-table.hh | 8 +- src/hb-subset-glyf.cc | 5 +- src/hb-subset-glyf.hh | 1 - src/hb-subset-plan.cc | 12 ++ src/hb-subset-plan.hh | 10 ++ src/hb-subset-private.hh | 11 ++ src/hb-subset.cc | 85 +++++---- test/api/Makefile.am | 7 + test/api/fonts/Inconsolata-Regular.ab.ttf | Bin 0 -> 5048 bytes test/api/fonts/Inconsolata-Regular.abc.ttf | Bin 0 -> 5224 bytes .../fonts/Inconsolata-Regular.abc.widerc.ttf | Bin 0 -> 5228 bytes test/api/fonts/Inconsolata-Regular.ac.ttf | Bin 0 -> 4940 bytes .../fonts/Inconsolata-Regular.ac.widerc.ttf | Bin 0 -> 4944 bytes test/api/fonts/README | 2 + test/api/test-subset-hmtx.c | 164 ++++++++++++++++++ 19 files changed, 422 insertions(+), 79 deletions(-) create mode 100644 test/api/fonts/Inconsolata-Regular.ab.ttf create mode 100644 test/api/fonts/Inconsolata-Regular.abc.ttf create mode 100644 test/api/fonts/Inconsolata-Regular.abc.widerc.ttf create mode 100644 test/api/fonts/Inconsolata-Regular.ac.ttf create mode 100644 test/api/fonts/Inconsolata-Regular.ac.widerc.ttf create mode 100644 test/api/test-subset-hmtx.c diff --git a/src/hb-ot-cmap-table.hh b/src/hb-ot-cmap-table.hh index c3f242db..6c5f3c28 100644 --- a/src/hb-ot-cmap-table.hh +++ b/src/hb-ot-cmap-table.hh @@ -599,13 +599,13 @@ struct cmap return true; } - inline hb_blob_t * subset (hb_subset_plan_t *plan, hb_face_t *source) const + inline hb_bool_t subset (hb_subset_plan_t *plan) const { hb_auto_array_t groups; if (unlikely(!populate_groups(plan, &groups))) { - return nullptr; + return false; } // We now know how big our blob needs to be @@ -617,21 +617,22 @@ struct cmap 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; + return false; } if (unlikely(!_subset(groups, dest_sz, dest))) { free(dest); - return nullptr; + return false; } // all done, write the blob into dest - return hb_blob_create ((const char *)dest, - dest_sz, - HB_MEMORY_MODE_READONLY, - /* userdata */ nullptr, - free); + hb_blob_t *cmap_prime = hb_blob_create ((const char *)dest, + dest_sz, + HB_MEMORY_MODE_READONLY, + /* userdata */ nullptr, + free); + return hb_subset_plan_add_table(plan, HB_OT_TAG_cmap, cmap_prime); } struct accelerator_t diff --git a/src/hb-ot-hhea-table.hh b/src/hb-ot-hhea-table.hh index 54eb5eb7..97952b4e 100644 --- a/src/hb-ot-hhea-table.hh +++ b/src/hb-ot-hhea-table.hh @@ -41,7 +41,7 @@ namespace OT { #define HB_OT_TAG_hhea HB_TAG('h','h','e','a') #define HB_OT_TAG_vhea HB_TAG('v','h','e','a') - +template struct _hea { inline bool sanitize (hb_sanitize_context_t *c) const @@ -84,10 +84,10 @@ struct _hea DEFINE_SIZE_STATIC (36); }; -struct hhea : _hea { +struct hhea : _hea { static const hb_tag_t tableTag = HB_OT_TAG_hhea; }; -struct vhea : _hea { +struct vhea : _hea { static const hb_tag_t tableTag = HB_OT_TAG_vhea; }; diff --git a/src/hb-ot-hmtx-table.hh b/src/hb-ot-hmtx-table.hh index b4ba2490..7fae4f6c 100644 --- a/src/hb-ot-hmtx-table.hh +++ b/src/hb-ot-hmtx-table.hh @@ -53,7 +53,7 @@ struct LongMetric DEFINE_SIZE_STATIC (4); }; -template +template struct hmtxvmtx { inline bool sanitize (hb_sanitize_context_t *c) const @@ -64,8 +64,129 @@ struct hmtxvmtx return_trace (true); } + + inline bool subset_update_header(hb_subset_plan_t *plan, + unsigned int num_hmetrics) const + { + /* alloc the new table */ + size_t dest_sz = sizeof(H); + void *dest = (void *) calloc(dest_sz, 1); + if (unlikely(!dest)) + { + return false; + } + + hb_blob_t *src_blob = OT::Sanitizer().sanitize (plan->source->reference_table (T::tableTag)); + if (unlikely(!src_blob)) + { + return false; + } + unsigned int src_length; + const char *src_raw = hb_blob_get_data(src_blob, &src_length); + + memcpy(dest, src_raw, src_length); + + H *table = (H *) dest; + table->numberOfLongMetrics.set(num_hmetrics); + + hb_blob_t *dest_blob = hb_blob_create ((const char *)dest, + dest_sz, + HB_MEMORY_MODE_READONLY, + /* userdata */ nullptr, + free); + return hb_subset_plan_add_table(plan, H::tableTag, dest_blob); + } + + inline bool subset (hb_subset_plan_t *plan) const + { + typename T::accelerator_t _mtx; + _mtx.init(plan->source); + + /* All the trailing glyphs with the same advance can use one LongMetric + * and just keep LSB */ + hb_prealloced_array_t &gids = plan->gids_to_retain_sorted; + unsigned int num_advances = gids.len; + unsigned int last_advance = _mtx.get_advance(gids[num_advances - 1]); + while (num_advances > 1 + && last_advance == _mtx.get_advance(gids[num_advances - 2])) + { + num_advances--; + } + + /* alloc the new table */ + size_t dest_sz = num_advances * 4 + + (gids.len - num_advances) * 2; + void *dest = (void *) calloc(dest_sz, 1); + if (unlikely(!dest)) + { + return false; + } + + DEBUG_MSG(SUBSET, nullptr, "%c%c%c%c in src has %d advances, %d lsbs", HB_UNTAG(T::tableTag), _mtx.num_advances, _mtx.num_metrics - _mtx.num_advances); + DEBUG_MSG(SUBSET, nullptr, "%c%c%c%c in dest has %d advances, %d lsbs, %d bytes", HB_UNTAG(T::tableTag), num_advances, gids.len - num_advances, dest_sz); + + const char *source_table = hb_blob_get_data(_mtx.blob, nullptr); + // Copy everything over + LongMetric * old_metrics = (LongMetric *) source_table; + FWORD *lsbs = (FWORD *) (old_metrics + _mtx.num_advances); + char * dest_pos = (char *) dest; + for (unsigned int i = 0; i < gids.len; i++) + { + /* the last metric or the one for gids[i] */ + LongMetric *src_metric = old_metrics + MIN(_mtx.num_advances - 1, gids[i]); + if (gids[i] < _mtx.num_advances) + { + /* src is a LongMetric */ + if (i < num_advances) + { + /* dest is a LongMetric, copy it */ + *((LongMetric *) dest_pos) = *src_metric; + } + else + { + /* dest just lsb */ + *((FWORD *) dest_pos) = src_metric->lsb; + } + } + else + { + FWORD src_lsb = *(lsbs + gids[i] - _mtx.num_advances); + if (i < num_advances) + { + /* dest needs a full LongMetric */ + LongMetric *metric = (LongMetric *)dest_pos; + metric->advance = src_metric->advance; + metric->lsb = src_lsb; + } + else + { + /* dest just needs an lsb */ + *((FWORD *) dest_pos) = src_lsb; + } + } + dest_pos += (i < num_advances ? 4 : 2); + } + _mtx.fini(); + + // Amend header num hmetrics + if (unlikely(!subset_update_header(plan, num_advances))) + { + free(dest); + return false; + } + + hb_blob_t *result = hb_blob_create ((const char *)dest, + dest_sz, + HB_MEMORY_MODE_READONLY, + /* userdata */ nullptr, + free); + return hb_subset_plan_add_table(plan, T::tableTag, result); + } + struct accelerator_t { + friend struct hmtxvmtx; + inline void init (hb_face_t *face, unsigned int default_advance_ = 0) { @@ -87,8 +208,8 @@ struct hmtxvmtx hb_blob_destroy (os2_blob); } - hb_blob_t *_hea_blob = Sanitizer<_hea>().sanitize (face->reference_table (T::headerTag)); - const _hea *_hea_table = Sanitizer<_hea>::lock_instance (_hea_blob); + hb_blob_t *_hea_blob = Sanitizer().sanitize (face->reference_table (H::tableTag)); + const H *_hea_table = Sanitizer::lock_instance (_hea_blob); num_advances = _hea_table->numberOfLongMetrics; if (!got_font_extents) { @@ -129,22 +250,27 @@ struct hmtxvmtx hb_blob_destroy (var_blob); } - inline unsigned int get_advance (hb_codepoint_t glyph, - hb_font_t *font) const + inline unsigned int get_advance (hb_codepoint_t glyph) const { if (unlikely (glyph >= num_metrics)) { - /* If num_metrics is zero, it means we don't have the metrics table - * for this direction: return default advance. Otherwise, it means that the - * glyph index is out of bound: return zero. */ - if (num_metrics) - return 0; - else - return default_advance; + /* If num_metrics is zero, it means we don't have the metrics table + * for this direction: return default advance. Otherwise, it means that the + * glyph index is out of bound: return zero. */ + if (num_metrics) + return 0; + else + return default_advance; } - return table->longMetric[MIN (glyph, (uint32_t) num_advances - 1)].advance - + (font->num_coords ? var_table->get_advance_var (glyph, font->coords, font->num_coords) : 0); // TODO Optimize?! + return table->longMetric[MIN (glyph, (uint32_t) num_advances - 1)].advance; + } + + inline unsigned int get_advance (hb_codepoint_t glyph, + hb_font_t *font) const + { + return get_advance(glyph) + + (font->num_coords ? var_table->get_advance_var (glyph, font->coords, font->num_coords) : 0); // TODO Optimize?! } public: @@ -153,11 +279,12 @@ struct hmtxvmtx unsigned short descender; unsigned short line_gap; - private: + protected: unsigned int num_metrics; unsigned int num_advances; unsigned int default_advance; + private: const hmtxvmtx *table; hb_blob_t *blob; const HVARVVAR *var_table; @@ -190,15 +317,13 @@ struct hmtxvmtx DEFINE_SIZE_ARRAY (0, longMetric); }; -struct hmtx : hmtxvmtx { +struct hmtx : hmtxvmtx { static const hb_tag_t tableTag = HB_OT_TAG_hmtx; - static const hb_tag_t headerTag = HB_OT_TAG_hhea; static const hb_tag_t variationsTag = HB_OT_TAG_HVAR; static const hb_tag_t os2Tag = HB_OT_TAG_os2; }; -struct vmtx : hmtxvmtx { +struct vmtx : hmtxvmtx { static const hb_tag_t tableTag = HB_OT_TAG_vmtx; - static const hb_tag_t headerTag = HB_OT_TAG_vhea; static const hb_tag_t variationsTag = HB_OT_TAG_VVAR; static const hb_tag_t os2Tag = HB_TAG_NONE; }; diff --git a/src/hb-ot-maxp-table.hh b/src/hb-ot-maxp-table.hh index 52b9388d..00a95d71 100644 --- a/src/hb-ot-maxp-table.hh +++ b/src/hb-ot-maxp-table.hh @@ -61,21 +61,21 @@ struct maxp (version.major == 0 && version.minor == 0x5000u))); } - inline hb_blob_t * subset (hb_subset_plan_t *plan, hb_face_t *source) const + inline hb_bool_t subset (hb_subset_plan_t *plan) const { - hb_blob_t *maxp_blob = OT::Sanitizer().sanitize (hb_face_reference_table (source, HB_OT_TAG_maxp)); + hb_blob_t *maxp_blob = OT::Sanitizer().sanitize (hb_face_reference_table (plan->source, HB_OT_TAG_maxp)); hb_blob_t *maxp_prime_blob = hb_blob_create_sub_blob (maxp_blob, 0, -1); hb_blob_destroy (maxp_blob); OT::maxp *maxp_prime = (OT::maxp *) hb_blob_get_data_writable (maxp_prime_blob, nullptr); if (unlikely (!maxp_prime)) { hb_blob_destroy (maxp_prime_blob); - return nullptr; + return false; } maxp_prime->set_num_glyphs (plan->gids_to_retain_sorted.len); - return maxp_prime_blob; + return hb_subset_plan_add_table(plan, HB_OT_TAG_maxp, maxp_prime_blob); } /* We only implement version 0.5 as none of the extra fields in version 1.0 are useful. */ diff --git a/src/hb-ot-os2-table.hh b/src/hb-ot-os2-table.hh index f80a05d7..1bb9a153 100644 --- a/src/hb-ot-os2-table.hh +++ b/src/hb-ot-os2-table.hh @@ -49,16 +49,16 @@ struct os2 return_trace (c->check_struct (this)); } - inline hb_blob_t * subset (hb_subset_plan_t *plan, hb_face_t *source) const + inline hb_bool_t subset (hb_subset_plan_t *plan) const { - hb_blob_t *os2_blob = OT::Sanitizer().sanitize (hb_face_reference_table (source, HB_OT_TAG_os2)); + hb_blob_t *os2_blob = OT::Sanitizer().sanitize (hb_face_reference_table (plan->source, HB_OT_TAG_os2)); hb_blob_t *os2_prime_blob = hb_blob_create_sub_blob (os2_blob, 0, -1); hb_blob_destroy (os2_blob); OT::os2 *os2_prime = (OT::os2 *) hb_blob_get_data_writable (os2_prime_blob, nullptr); if (unlikely (!os2_prime)) { hb_blob_destroy (os2_prime_blob); - return nullptr; + return false; } uint16_t min_cp, max_cp; @@ -66,7 +66,7 @@ struct os2 os2_prime->usFirstCharIndex.set (min_cp); os2_prime->usLastCharIndex.set (max_cp); - return os2_prime_blob; + return hb_subset_plan_add_table(plan, HB_OT_TAG_os2, os2_prime_blob); } static inline void find_min_and_max_codepoint (hb_subset_plan_t *plan, diff --git a/src/hb-subset-glyf.cc b/src/hb-subset-glyf.cc index 36789f9b..f1bca10d 100644 --- a/src/hb-subset-glyf.cc +++ b/src/hb-subset-glyf.cc @@ -157,16 +157,15 @@ _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, 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)); + hb_blob_t *glyf_blob = OT::Sanitizer().sanitize (plan->source->reference_table (HB_OT_TAG_glyf)); const char *glyf_data = hb_blob_get_data(glyf_blob, nullptr); OT::glyf::accelerator_t glyf; - glyf.init(face); + glyf.init(plan->source); bool result = _hb_subset_glyf_and_loca (glyf, glyf_data, plan->gids_to_retain_sorted, diff --git a/src/hb-subset-glyf.hh b/src/hb-subset-glyf.hh index 491bf82b..99b76db9 100644 --- a/src/hb-subset-glyf.hh +++ b/src/hb-subset-glyf.hh @@ -33,7 +33,6 @@ HB_INTERNAL 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-plan.cc b/src/hb-subset-plan.cc index 6f4b003b..0381e25c 100644 --- a/src/hb-subset-plan.cc +++ b/src/hb-subset-plan.cc @@ -72,6 +72,14 @@ hb_subset_plan_new_gid_for_old_id (hb_subset_plan_t *plan, return false; } +hb_bool_t +hb_subset_plan_add_table (hb_subset_plan_t *plan, + hb_tag_t tag, + hb_blob_t *contents) +{ + return hb_subset_face_add_table(plan->dest, tag, contents); +} + static void _populate_codepoints (hb_set_t *input_codepoints, hb_prealloced_array_t& plan_codepoints) @@ -110,6 +118,7 @@ _populate_gids_to_retain (hb_face_t *face, *(old_gids.push ()) = gid; } + /* Generally there shouldn't be any */ while (bad_indices.len > 0) { unsigned int i = bad_indices[bad_indices.len - 1]; bad_indices.pop (); @@ -154,12 +163,15 @@ hb_subset_plan_create (hb_face_t *face, plan->codepoints.init(); plan->gids_to_retain.init(); plan->gids_to_retain_sorted.init(); + plan->source = face; + plan->dest = hb_subset_face_create (); _populate_codepoints (input->unicodes, plan->codepoints); _populate_gids_to_retain (face, plan->codepoints, plan->gids_to_retain, plan->gids_to_retain_sorted); + return plan; } diff --git a/src/hb-subset-plan.hh b/src/hb-subset-plan.hh index 09df7cdd..0a43eb90 100644 --- a/src/hb-subset-plan.hh +++ b/src/hb-subset-plan.hh @@ -38,9 +38,14 @@ struct hb_subset_plan_t { // 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. + // TODO Also you should init/fini those arrays hb_prealloced_array_t codepoints; hb_prealloced_array_t gids_to_retain; hb_prealloced_array_t gids_to_retain_sorted; + + // Plan is only good for a specific source/dest so keep them with it + hb_face_t *source; + hb_face_t *dest; }; typedef struct hb_subset_plan_t hb_subset_plan_t; @@ -60,6 +65,11 @@ hb_subset_plan_new_gid_for_codepoint(hb_subset_plan_t *plan, hb_codepoint_t codepont, hb_codepoint_t *new_gid /* OUT */); +HB_INTERNAL hb_bool_t +hb_subset_plan_add_table(hb_subset_plan_t *plan, + hb_tag_t tag, + hb_blob_t *contents); + HB_INTERNAL void hb_subset_plan_destroy (hb_subset_plan_t *plan); diff --git a/src/hb-subset-private.hh b/src/hb-subset-private.hh index 4ef1b954..3d6f8b4a 100644 --- a/src/hb-subset-private.hh +++ b/src/hb-subset-private.hh @@ -34,6 +34,8 @@ #include "hb-font-private.hh" +typedef struct hb_subset_face_data_t hb_subset_face_data_t; + struct hb_subset_input_t { hb_object_header_t header; ASSERT_POD (); @@ -50,4 +52,13 @@ struct hb_subset_input_t { */ }; +HB_INTERNAL hb_face_t * +hb_subset_face_create (void); + +HB_INTERNAL hb_bool_t +hb_subset_face_add_table (hb_face_t *face, hb_tag_t tag, hb_blob_t *blob); + +HB_INTERNAL void +hb_subset_face_data_destroy (void *user_data); + #endif /* HB_SUBSET_PRIVATE_HH */ diff --git a/src/hb-subset.cc b/src/hb-subset.cc index e7f2d912..4de146ea 100644 --- a/src/hb-subset.cc +++ b/src/hb-subset.cc @@ -21,12 +21,14 @@ * 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, Rod Sheeter, Behdad Esfahbod + * Google Author(s): Garret Rieger, Rod Sheeter */ #include "hb-object-private.hh" #include "hb-open-type-private.hh" +#include "hb-private.hh" + #include "hb-subset-glyf.hh" #include "hb-subset-private.hh" #include "hb-subset-plan.hh" @@ -35,6 +37,8 @@ #include "hb-ot-cmap-table.hh" #include "hb-ot-glyf-table.hh" #include "hb-ot-head-table.hh" +#include "hb-ot-hhea-table.hh" +#include "hb-ot-hmtx-table.hh" #include "hb-ot-maxp-table.hh" #include "hb-ot-os2-table.hh" @@ -76,13 +80,13 @@ hb_subset_profile_destroy (hb_subset_profile_t *profile) } template -static hb_blob_t * -_subset (hb_subset_plan_t *plan, hb_face_t *source) +static hb_bool_t +_subset (hb_subset_plan_t *plan) { OT::Sanitizer sanitizer; - hb_blob_t *source_blob = sanitizer.sanitize (source->reference_table (TableType::tableTag)); + hb_blob_t *source_blob = sanitizer.sanitize (plan->source->reference_table (TableType::tableTag)); const TableType *table = OT::Sanitizer::lock_instance (source_blob); - hb_blob_t *result = table->subset(plan, source); + hb_bool_t result = table->subset(plan); hb_blob_destroy (source_blob); @@ -124,8 +128,8 @@ _hb_subset_face_data_create (void) return data; } -static void -_hb_subset_face_data_destroy (void *user_data) +void +hb_subset_face_data_destroy (void *user_data) { hb_subset_face_data_t *data = (hb_subset_face_data_t *) user_data; @@ -188,7 +192,7 @@ _hb_subset_face_reference_table (hb_face_t *face, hb_tag_t tag, void *user_data) return nullptr; } -static hb_face_t * +hb_face_t * hb_subset_face_create (void) { hb_subset_face_data_t *data = _hb_subset_face_data_create (); @@ -196,13 +200,13 @@ hb_subset_face_create (void) return hb_face_create_for_tables (_hb_subset_face_reference_table, data, - _hb_subset_face_data_destroy); + hb_subset_face_data_destroy); } -static bool +hb_bool_t hb_subset_face_add_table (hb_face_t *face, hb_tag_t tag, hb_blob_t *blob) { - if (unlikely (face->destroy != _hb_subset_face_data_destroy)) + if (unlikely (face->destroy != hb_subset_face_data_destroy)) return false; hb_subset_face_data_t *data = (hb_subset_face_data_t *) face->user_data; @@ -221,7 +225,7 @@ _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); + hb_bool_t has_head = (head != nullptr); if (has_head) { OT::head *head_prime = (OT::head *) calloc (OT::head::static_size, 1); @@ -242,7 +246,7 @@ _add_head_and_set_loca_version (hb_face_t *source, bool use_short_loca, hb_face_ } static bool -_subset_glyf (hb_subset_plan_t *plan, hb_face_t *source, hb_face_t *dest) +_subset_glyf (hb_subset_plan_t *plan) { hb_blob_t *glyf_prime = nullptr; hb_blob_t *loca_prime = nullptr; @@ -250,10 +254,10 @@ _subset_glyf (hb_subset_plan_t *plan, hb_face_t *source, hb_face_t *dest) 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, &use_short_loca, &glyf_prime, &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); + if (hb_subset_glyf_and_loca (plan, &use_short_loca, &glyf_prime, &loca_prime)) { + success = success && hb_subset_plan_add_table (plan, HB_OT_TAG_glyf, glyf_prime); + success = success && hb_subset_plan_add_table (plan, HB_OT_TAG_loca, loca_prime); + success = success && _add_head_and_set_loca_version (plan->source, use_short_loca, plan->dest); } else { success = false; } @@ -265,39 +269,50 @@ _subset_glyf (hb_subset_plan_t *plan, hb_face_t *source, hb_face_t *dest) static bool _subset_table (hb_subset_plan_t *plan, - hb_face_t *source, - hb_tag_t tag, - hb_blob_t *source_blob, - hb_face_t *dest) + hb_tag_t tag) { // 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; + bool result = true; switch (tag) { case HB_OT_TAG_glyf: - return _subset_glyf (plan, source, dest); + result = _subset_glyf (plan); + break; case HB_OT_TAG_head: // SKIP head, it's handled by glyf + result = true; + break; + case HB_OT_TAG_hhea: + // SKIP hhea, it's handled by hmtx return true; + case HB_OT_TAG_hmtx: + result = _subset (plan); + break; case HB_OT_TAG_maxp: - dest_blob = _subset (plan, source); + result = _subset (plan); break; case HB_OT_TAG_loca: // SKIP loca, it's handle by glyf return true; case HB_OT_TAG_cmap: - dest_blob = _subset (plan, source); + result = _subset (plan); break; case HB_OT_TAG_os2: - dest_blob = _subset (plan, source); + result = _subset (plan); break; default: - dest_blob = source_blob; + hb_blob_t *source_table = hb_face_reference_table(plan->source, tag); + if (likely(source_table)) + { + result = hb_subset_plan_add_table(plan, tag, source_table); + } + else + { + result = false; + } 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; + DEBUG_MSG(SUBSET, nullptr, "subset %c%c%c%c %s", HB_UNTAG(tag), result ? "ok" : "FAILED"); return true; } @@ -325,14 +340,13 @@ _should_drop_table(hb_tag_t tag) **/ hb_face_t * hb_subset (hb_face_t *source, - hb_subset_profile_t *profile, + hb_subset_profile_t *profile, hb_subset_input_t *input) { if (unlikely (!profile || !input || !source)) return hb_face_get_empty(); hb_subset_plan_t *plan = hb_subset_plan_create (source, profile, input); - hb_face_t *dest = hb_subset_face_create (); hb_tag_t table_tags[32]; unsigned int offset = 0, count; bool success = true; @@ -347,12 +361,11 @@ hb_subset (hb_face_t *source, DEBUG_MSG(SUBSET, nullptr, "drop %c%c%c%c", HB_UNTAG(tag)); continue; } - hb_blob_t *blob = hb_face_reference_table (source, tag); - success = success && _subset_table (plan, source, tag, blob, dest); - hb_blob_destroy (blob); + success = success && _subset_table (plan, tag); } } while (count == ARRAY_LENGTH (table_tags)); + hb_face_t *result = success ? hb_face_reference(plan->dest) : hb_face_get_empty(); hb_subset_plan_destroy (plan); - return success ? dest : hb_face_get_empty (); + return result; } diff --git a/test/api/Makefile.am b/test/api/Makefile.am index 071264e1..dafbb0e4 100644 --- a/test/api/Makefile.am +++ b/test/api/Makefile.am @@ -31,6 +31,7 @@ TEST_PROGS = \ test-shape \ test-subset-cmap \ test-subset-glyf \ + test-subset-hmtx \ test-subset-os2 \ test-unicode \ test-version \ @@ -39,6 +40,7 @@ TEST_PROGS = \ test_subset_LDADD = $(LDADD) $(top_builddir)/src/libharfbuzz-subset.la test_subset_cmap_LDADD = $(LDADD) $(top_builddir)/src/libharfbuzz-subset.la test_subset_glyf_LDADD = $(LDADD) $(top_builddir)/src/libharfbuzz-subset.la +test_subset_hmtx_LDADD = $(LDADD) $(top_builddir)/src/libharfbuzz-subset.la test_subset_os2_LDADD = $(LDADD) $(top_builddir)/src/libharfbuzz-subset.la test_unicode_CPPFLAGS = \ @@ -65,6 +67,11 @@ TEST_PROGS += \ test_ot_math_LDADD = $(LDADD) $(FREETYPE_LIBS) test_ot_math_CPPFLAGS = $(AM_CPPFLAGS) $(FREETYPE_CFLAGS) EXTRA_DIST += \ + fonts/Inconsolata-Regular.ab.ttf \ + fonts/Inconsolata-Regular.abc.ttf \ + fonts/Inconsolata-Regular.abc.widerc.ttf \ + fonts/Inconsolata-Regular.ac.ttf \ + fonts/Inconsolata-Regular.ac.widerc.ttf \ fonts/Roboto-Regular.abc.ttf \ fonts/Roboto-Regular.ac.ttf \ fonts/MathTestFontEmpty.otf \ diff --git a/test/api/fonts/Inconsolata-Regular.ab.ttf b/test/api/fonts/Inconsolata-Regular.ab.ttf new file mode 100644 index 0000000000000000000000000000000000000000..455cc15f462b88190117a0469454a90fc593eef9 GIT binary patch literal 5048 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 literal 0 HcmV?d00001 diff --git a/test/api/fonts/Inconsolata-Regular.abc.widerc.ttf b/test/api/fonts/Inconsolata-Regular.abc.widerc.ttf new file mode 100644 index 0000000000000000000000000000000000000000..352f269922339f1207c5810b932b989786bbd05a GIT binary patch literal 5228 zcmbVQTWnlM8J;=k?8SC`i@oc_YuB?rUfcDa-6X!nX*S7bFLsi36Wejpa}rwbW^E^h zT-@B6mX;Q1X}Snh2_9OhLP&@Q5JIy~X#*-K4?IvFKxz>@@B(iL6$y#7AP`-?f6lIJ zrwOULme2hE{NMcZ-{zk^%Q$1K6%T{8Ol7i@tQ8b%_hU9VHFM->=IZnn@B+M;I(jU1 z^WQgbgHM7VI&yT|@Z#|+zhI2N3O-*vS6E@2*qe-Lw0^XB^|BaS(1#go{ulUHmsd`o zySn(?(~u_^YYd()Tw1|=3-n=N{?lilTK@YVr>nseja?^nRAz~`A6CJ!IS=$vlog5t(!+dz5>4fT;bXZ-`{u!zH)JCW!L!O3$T~;e~(dYG{(O4qsY4p&4~|~uKxuy?7hD| z*-Yc_f7tap+fn&|zoWed)<;r3>IO!;x*RC#@N?E zztk91a|XZ7Utu2TC_xKjId|lbv0=WM)_CgZsXk=q+9&PIW!WuOU`5vk{e31V#gY#1|_SJPVad<-3Y!l37BsV|LaN zB6pKD&E}-{=m}enhUB1=Ulyffw$vhppZb}f6^rJnP&h1^BU#$atU^ONpNdItNs(WU zNzD?ALf)E@M*oS`L0+HE7PHcuwZl@6ICCfMuo(`OY$0c6FqL#dLQW7h;W(n~q6>?1 z5K~o7$n7NGPRLs`wtzLIf{^tyb{;c=Wc5Ui6E&U>I?ei&7s7LzosNQNjl0 z?VbpnoVD{ap~9SFn@-pfGI`X7EJUtV_m4@BC4K4WD)OQtMa7GSD5g?`U}VioWXCLI+$|dJv0V0H6&A0#wtmqjDj_qq+EA|}*&#FR425H| z(JE_NRxTDYG1+9n0U_k3^da&Kgqd<=BXM)!8o|Y6Gt{)GkO)Me2(x8VIxk9jA)64i zm~63f^LE)-%s73raml6*3KPuCqrS(x2gHfR+%-YkJ;ts=CtI6lx&Vt&7zW1 z@pQHf|V*EzqIX8*u=6LjcOn{Ex}PBsRK`@VE+_mH>T9E5)~T9!zt;}f$G*4Dg8Be!{~g#t!tQLV-; z8H>soPT@8jfGJe!U5}wzm@ug${$B*jFebOJrIrWfwxP0z2eLLA86|MX`pA4zJZyUTMCSr2TigzSpa{PW! zRB#dSoyZ9bMnqgp(aA?F50*+(<`g=OjVl73742Z0^FTX7I)N4ylr4Z6=$a7)lXdCn zQYmfEur0-blz0Pb8|;7sMTwKw|-mBgzOoacNPj{CF>v#zVC@2w33hug1YlAVj;@MADCLA;hFP zMVnIg5;d7}rW#>3Z^uOfS1wtvxsy=(aSFT6W;Ii=HzxPgc2m)> zrYcWg0q%6UmfpQ+6mg2;q-;ssGa+1=BH_f#ao&ytx^IJYE;O@2n%p3*tzED0&K3JD zIT2kCs4c2(n6l)qXbGi9^;N==zL#xCXk5l&Vp_RONt;0mEy zS9SjBi+Jj8CG}bAmLg0oDq&89!s{v|>{M;cqPI>&YjKzaw<{W^EKww?o~$`N1*Ze< z0ms)3oaMH-9L0G)aF6+eP{-Tb8Q{Bb=fhr9IPtJw|&Hu|Rtgk*7V0C=g~6#3{lMQ6vlzi-aL!N!gVG zwXEzS>a?@0hWwkj&D__Yl``|C z-k2Kc(b*7@Z$0Gi_iy&=ewWL5w|l$1imOk(-E^z&mT?QOVReu;rru^r^{059Wy*d0 z`YgWSU$@KpVy4_r+|T^aGj1fW7x66@5WLMeZzcSL{&v5+ zCIchy^OsYyFuUc?4MQKywXQzeTPT+a~L)CcKvhy9^1-H_S|AIiz5 z89ONe^msMTX}q8t3qEcbJvl%3df5U_E(@^fP)@c0*LN2jb_pmjU%-*>=7jC|R60MM zI^-c4=4qa)%t&uroo`E&1p@(d%xm`ckByFxj|_M8_nW<5-@y3D=JB0G z2HHpX@zaNgE>0ic`}n?Hac?%$yRXws7PlWaVx!Y`fxox>^gkwNv%9t*81O#4uXAfW z`H}O;oNss}2Uio-UnT-fu$yl-Xqw01)rfV&*IottaQ>9uHga>e~8nh3^2V zP!UpJ@N!-6)@!l4kGACf6;YJhgF*b}B!WF%oyb}HKx>=d*AWf&5BSX1k$~452vCrM zfp)Lg93LMWtw!k2lUugN=e&9_H*kD;+};s8klr;h`AkEkH=(`e^ES2qHDX=6boj!~ zm2V)oN z3{Q(k)5a%u9N2poX$`M- zL3WUxXGL}a&r5io#V~+uK+&) literal 0 HcmV?d00001 diff --git a/test/api/fonts/Inconsolata-Regular.ac.ttf b/test/api/fonts/Inconsolata-Regular.ac.ttf new file mode 100644 index 0000000000000000000000000000000000000000..991b8de4a95d27489888c4ae2d78ba64ad01b278 GIT binary patch literal 4940 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 literal 0 HcmV?d00001 diff --git a/test/api/fonts/Inconsolata-Regular.ac.widerc.ttf b/test/api/fonts/Inconsolata-Regular.ac.widerc.ttf new file mode 100644 index 0000000000000000000000000000000000000000..2050c28dcc43bb492b48e998fb12b82490f71f39 GIT binary patch literal 4944 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 literal 0 HcmV?d00001 diff --git a/test/api/fonts/README b/test/api/fonts/README index a365afd7..7e7783c2 100644 --- a/test/api/fonts/README +++ b/test/api/fonts/README @@ -1 +1,3 @@ cmap-format12-only files created by ttx & remove all other cmap entries + +Inconsolata-Regular.abc.widerc.ttf has the hmtx width of "c" set to 600; everything else is 500. Subsetting out c should reduce numberOfHMetrics to 1. diff --git a/test/api/test-subset-hmtx.c b/test/api/test-subset-hmtx.c new file mode 100644 index 00000000..24a99c37 --- /dev/null +++ b/test/api/test-subset-hmtx.c @@ -0,0 +1,164 @@ +/* + * Copyright © 2018 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Roderick Sheeter + */ + +#include + +#include "hb-test.h" +#include "hb-subset-test.h" + +/* Unit tests for hmtx subsetting */ + +static void check_num_hmetrics(hb_face_t *face, uint16_t expected_num_hmetrics) +{ + hb_blob_t *hhea_blob = hb_face_reference_table (face, HB_TAG ('h','h','e','a')); + hb_blob_t *hmtx_blob = hb_face_reference_table (face, HB_TAG ('h','m','t','x')); + + // TODO I sure wish I could just use the hmtx table struct! + unsigned int hmtx_len = hb_blob_get_length(hmtx_blob); + unsigned int hhea_len; + uint8_t *raw_hhea = (uint8_t *) hb_blob_get_data(hhea_blob, &hhea_len); + uint16_t num_hmetrics = (raw_hhea[hhea_len - 2] << 8) + raw_hhea[hhea_len - 1]; + g_assert_cmpuint(expected_num_hmetrics, ==, num_hmetrics); + + hb_blob_destroy (hhea_blob); + hb_blob_destroy (hmtx_blob); +} + +static void +test_subset_hmtx_simple_subset (void) +{ + hb_face_t *face_abc = hb_subset_test_open_font ("fonts/Roboto-Regular.abc.ttf"); + hb_face_t *face_ac = hb_subset_test_open_font ("fonts/Roboto-Regular.ac.ttf"); + + hb_set_t *codepoints = hb_set_create (); + hb_set_add (codepoints, 'a'); + hb_set_add (codepoints, 'c'); + hb_face_t *face_abc_subset = hb_subset_test_create_subset (face_abc, codepoints); + hb_set_destroy (codepoints); + + check_num_hmetrics(face_abc_subset, 3); /* nothing has same width */ + hb_subset_test_check (face_ac, face_abc_subset, HB_TAG ('h','m','t','x')); + + hb_face_destroy (face_abc_subset); + hb_face_destroy (face_abc); + hb_face_destroy (face_ac); +} + + +static void +test_subset_hmtx_monospace (void) +{ + hb_face_t *face_abc = hb_subset_test_open_font ("fonts/Inconsolata-Regular.abc.ttf"); + hb_face_t *face_ac = hb_subset_test_open_font ("fonts/Inconsolata-Regular.ac.ttf"); + + hb_set_t *codepoints = hb_set_create (); + hb_set_add (codepoints, 'a'); + hb_set_add (codepoints, 'c'); + hb_face_t *face_abc_subset = hb_subset_test_create_subset (face_abc, codepoints); + hb_set_destroy (codepoints); + + check_num_hmetrics(face_abc_subset, 1); /* everything has same width */ + hb_subset_test_check (face_ac, face_abc_subset, HB_TAG ('h','m','t','x')); + // TODO hhea + + hb_face_destroy (face_abc_subset); + hb_face_destroy (face_abc); + hb_face_destroy (face_ac); +} + + +static void +test_subset_hmtx_keep_num_metrics (void) +{ + hb_face_t *face_abc = hb_subset_test_open_font ("fonts/Inconsolata-Regular.abc.widerc.ttf"); + hb_face_t *face_ac = hb_subset_test_open_font ("fonts/Inconsolata-Regular.ac.widerc.ttf"); + + hb_set_t *codepoints = hb_set_create (); + hb_set_add (codepoints, 'a'); + hb_set_add (codepoints, 'c'); + hb_face_t *face_abc_subset = hb_subset_test_create_subset (face_abc, codepoints); + hb_set_destroy (codepoints); + + check_num_hmetrics(face_abc_subset, 3); /* c is wider */ + hb_subset_test_check (face_ac, face_abc_subset, HB_TAG ('h','m','t','x')); + + hb_face_destroy (face_abc_subset); + hb_face_destroy (face_abc); + hb_face_destroy (face_ac); +} + +static void +test_subset_hmtx_decrease_num_metrics (void) +{ + hb_face_t *face_abc = hb_subset_test_open_font ("fonts/Inconsolata-Regular.abc.widerc.ttf"); + hb_face_t *face_ab = hb_subset_test_open_font ("fonts/Inconsolata-Regular.ab.ttf"); + + hb_set_t *codepoints = hb_set_create (); + hb_set_add (codepoints, 'a'); + hb_set_add (codepoints, 'b'); + hb_face_t *face_abc_subset = hb_subset_test_create_subset (face_abc, codepoints); + hb_set_destroy (codepoints); + + check_num_hmetrics(face_abc_subset, 1); /* everything left has same width */ + hb_subset_test_check (face_ab, face_abc_subset, HB_TAG ('h','m','t','x')); + + hb_face_destroy (face_abc_subset); + hb_face_destroy (face_abc); + hb_face_destroy (face_ab); +} + +static void +test_subset_hmtx_noop (void) +{ + hb_face_t *face_abc = hb_subset_test_open_font("fonts/Roboto-Regular.abc.ttf"); + + hb_set_t *codepoints = hb_set_create(); + hb_set_add (codepoints, 'a'); + hb_set_add (codepoints, 'b'); + hb_set_add (codepoints, 'c'); + hb_face_t *face_abc_subset = hb_subset_test_create_subset (face_abc, codepoints); + hb_set_destroy (codepoints); + + check_num_hmetrics(face_abc_subset, 4); /* nothing has same width */ + hb_subset_test_check (face_abc, face_abc_subset, HB_TAG ('h','m','t','x')); + + hb_face_destroy (face_abc_subset); + hb_face_destroy (face_abc); +} + +int +main (int argc, char **argv) +{ + hb_test_init (&argc, &argv); + + hb_test_add (test_subset_hmtx_simple_subset); + hb_test_add (test_subset_hmtx_monospace); + hb_test_add (test_subset_hmtx_keep_num_metrics); + hb_test_add (test_subset_hmtx_decrease_num_metrics); + hb_test_add (test_subset_hmtx_noop); + + return hb_test_run(); +} -- GitLab