From 09df17e71b1860e250638e8e76deee1da5e7a06b Mon Sep 17 00:00:00 2001 From: Michiharu Ariza Date: Wed, 20 Feb 2019 15:48:29 -0800 Subject: [PATCH 01/16] subset HVAR Re-implemented & repurposed CFF:remap_t as hb_map2_t (moved to hb-ot-layout-common.hh) for two-way mapping for use by index map subsetting. Hooked up HVAR subsetter through _subset2. Some renaming in CFF code. --- src/hb-ot-cff-common.hh | 63 +--------- src/hb-ot-cff1-table.hh | 8 +- src/hb-ot-layout-common.hh | 158 +++++++++++++++++++++++- src/hb-ot-var-hvar-table.hh | 232 +++++++++++++++++++++++++++++++++++- src/hb-subset-cff-common.cc | 9 +- src/hb-subset-cff-common.hh | 27 ++--- src/hb-subset-cff1.cc | 29 ++--- src/hb-subset-cff2.cc | 12 +- src/hb-subset.cc | 4 + 9 files changed, 426 insertions(+), 116 deletions(-) diff --git a/src/hb-ot-cff-common.hh b/src/hb-ot-cff-common.hh index c645953e5..5e0082d20 100644 --- a/src/hb-ot-cff-common.hh +++ b/src/hb-ot-cff-common.hh @@ -104,7 +104,7 @@ struct CFFIndex else return min_size + calculate_offset_array_size (offSize, count) + dataSize; } - + bool serialize (hb_serialize_context_t *c, const CFFIndex &src) { TRACE_SERIALIZE (this); @@ -414,57 +414,6 @@ struct table_info_t unsigned int offSize; }; -/* used to remap font index or SID from fullset to subset. - * set to CFF_UNDEF_CODE if excluded from subset */ -struct remap_t : hb_vector_t -{ - void init () { SUPER::init (); } - - void fini () { SUPER::fini (); } - - bool reset (unsigned int size) - { - if (unlikely (!SUPER::resize (size))) - return false; - for (unsigned int i = 0; i < length; i++) - (*this)[i] = CFF_UNDEF_CODE; - count = 0; - return true; - } - - bool identity (unsigned int size) - { - if (unlikely (!SUPER::resize (size))) - return false; - unsigned int i; - for (i = 0; i < length; i++) - (*this)[i] = i; - count = i; - return true; - } - - bool excludes (hb_codepoint_t id) const - { return (id < length) && ((*this)[id] == CFF_UNDEF_CODE); } - - bool includes (hb_codepoint_t id) const - { return !excludes (id); } - - unsigned int add (unsigned int i) - { - if ((*this)[i] == CFF_UNDEF_CODE) - (*this)[i] = count++; - return (*this)[i]; - } - - hb_codepoint_t get_count () const { return count; } - - protected: - hb_codepoint_t count; - - private: - typedef hb_vector_t SUPER; -}; - template struct FDArray : CFFIndexOf { @@ -508,7 +457,7 @@ struct FDArray : CFFIndexOf unsigned int offSize_, const hb_vector_t &fontDicts, unsigned int fdCount, - const remap_t &fdmap, + const hb_map2_t &fdmap, OP_SERIALIZER& opszr, const hb_vector_t &privateInfos) { @@ -523,7 +472,7 @@ struct FDArray : CFFIndexOf unsigned int offset = 1; unsigned int fid = 0; for (unsigned i = 0; i < fontDicts.length; i++) - if (fdmap.includes (i)) + if (fdmap.has (i)) { CFFIndexOf::set_offset_at (fid++, offset); offset += FontDict::calculate_serialized_size (fontDicts[i], opszr); @@ -532,7 +481,7 @@ struct FDArray : CFFIndexOf /* serialize font dicts */ for (unsigned int i = 0; i < fontDicts.length; i++) - if (fdmap.includes (i)) + if (fdmap.has (i)) { FontDict *dict = c->start_embed (); if (unlikely (!dict->serialize (c, fontDicts[i], opszr, privateInfos[fdmap[i]]))) @@ -546,12 +495,12 @@ struct FDArray : CFFIndexOf static unsigned int calculate_serialized_size (unsigned int &offSize_ /* OUT */, const hb_vector_t &fontDicts, unsigned int fdCount, - const remap_t &fdmap, + const hb_map2_t &fdmap, OP_SERIALIZER& opszr) { unsigned int dictsSize = 0; for (unsigned int i = 0; i < fontDicts.len; i++) - if (fdmap.includes (i)) + if (fdmap.has (i)) dictsSize += FontDict::calculate_serialized_size (fontDicts[i], opszr); offSize_ = calcOffSize (dictsSize); diff --git a/src/hb-ot-cff1-table.hh b/src/hb-ot-cff1-table.hh index 9d39242cf..d65b2250a 100644 --- a/src/hb-ot-cff1-table.hh +++ b/src/hb-ot-cff1-table.hh @@ -570,7 +570,7 @@ struct Charset { struct CFF1StringIndex : CFF1Index { bool serialize (hb_serialize_context_t *c, const CFF1StringIndex &strings, - unsigned int offSize_, const remap_t &sidmap) + unsigned int offSize_, const hb_map2_t &sidmap) { TRACE_SERIALIZE (this); if (unlikely ((strings.count == 0) || (sidmap.get_count () == 0))) @@ -588,7 +588,7 @@ struct CFF1StringIndex : CFF1Index for (unsigned int i = 0; i < strings.count; i++) { hb_codepoint_t j = sidmap[i]; - if (j != CFF_UNDEF_CODE) + if (j != HB_MAP_VALUE_INVALID) bytesArray[j] = strings[i]; } @@ -598,7 +598,7 @@ struct CFF1StringIndex : CFF1Index } /* in parallel to above */ - unsigned int calculate_serialized_size (unsigned int &offSize /*OUT*/, const remap_t &sidmap) const + unsigned int calculate_serialized_size (unsigned int &offSize /*OUT*/, const hb_map2_t &sidmap) const { offSize = 0; if ((count == 0) || (sidmap.get_count () == 0)) @@ -606,7 +606,7 @@ struct CFF1StringIndex : CFF1Index unsigned int dataSize = 0; for (unsigned int i = 0; i < count; i++) - if (sidmap[i] != CFF_UNDEF_CODE) + if (sidmap[i] != HB_MAP_VALUE_INVALID) dataSize += length_at (i); offSize = calcOffSize(dataSize); diff --git a/src/hb-ot-layout-common.hh b/src/hb-ot-layout-common.hh index 9b172257b..c41865ff2 100644 --- a/src/hb-ot-layout-common.hh +++ b/src/hb-ot-layout-common.hh @@ -1586,6 +1586,82 @@ static inline void ClassDef_serialize (hb_serialize_context_t *c, hb_array_t klasses) { c->start_embed ()->serialize (c, glyphs, klasses); } +struct hb_map2_t +{ + hb_map2_t () { init (); } + ~hb_map2_t () { fini (); } + + void init (void) + { + count = 0; + old_to_new_map.init (); + new_to_old_map.init (); + set.init (); + } + + void fini (void) + { + old_to_new_map.fini (); + new_to_old_map.fini (); + set.fini (); + } + + bool has (hb_codepoint_t id) const { return set.has (id); } + + hb_codepoint_t add (hb_codepoint_t i) + { + hb_codepoint_t v = old_to_new_map[i]; + if (v == HB_MAP_VALUE_INVALID) + { + set.add (i); + v = count++; + old_to_new_map.set (i, v); + new_to_old_map.set (v, i); + } + return v; + } + + /* returns HB_MAP_VALUE_INVALID if unmapped */ + hb_codepoint_t operator [] (hb_codepoint_t i) const { return old_to_new (i); } + hb_codepoint_t old_to_new (hb_codepoint_t i) const { return old_to_new_map[i]; } + hb_codepoint_t new_to_old (hb_codepoint_t i) const { return new_to_old_map[i]; } + + bool identity (unsigned int size) + { + hb_codepoint_t i; + old_to_new_map.clear (); + new_to_old_map.clear (); + set.clear (); + for (i = 0; i < size; i++) + { + old_to_new_map.set (i, i); + new_to_old_map.set (i, i); + set.add (i); + } + count = i; + return old_to_new_map.successful && new_to_old_map.successful && set.successful; + } + + /* Optional: after finished adding all mappings in a random order, + * reorder outputs in the same order as the inputs. */ + void reorder (void) + { + for (hb_codepoint_t i = HB_SET_VALUE_INVALID, count = 0; set.next (&i); count++) + { + new_to_old_map.set (count, i); + old_to_new_map.set (i, count); + } + } + + unsigned int get_count () const { return count; } + unsigned int get_bits () const { return count? hb_bit_storage (count - 1): 0; } + + protected: + unsigned int count; + hb_map_t old_to_new_map; + hb_map_t new_to_old_map; + hb_set_t set; +}; /* * Item Variation Store @@ -1662,6 +1738,15 @@ struct VarRegionList axesZ.sanitize (c, (unsigned int) axisCount * (unsigned int) regionCount)); } + bool serialize (hb_serialize_context_t *c, const VarRegionList *src) + { + TRACE_SERIALIZE (this); + if (unlikely (!c->allocate_size (src->get_size ()))) return_trace (false); + memcpy (this, src, src->get_size ()); + return_trace (true); + } + + unsigned int get_size () const { return min_size + VarRegionAxis::static_size * axisCount * regionCount; } unsigned int get_region_count () const { return regionCount; } protected: @@ -1678,9 +1763,6 @@ struct VarData unsigned int get_region_index_count () const { return regionIndices.len; } - unsigned int get_row_size () const - { return shortCount + regionIndices.len; } - unsigned int get_size () const { return itemCount * get_row_size (); } @@ -1694,7 +1776,7 @@ struct VarData unsigned int count = regionIndices.len; unsigned int scount = shortCount; - const HBUINT8 *bytes = &StructAfter (regionIndices); + const HBUINT8 *bytes = get_delta_bytes (); const HBUINT8 *row = bytes + inner * (scount + count); float delta = 0.; @@ -1734,11 +1816,45 @@ struct VarData return_trace (c->check_struct (this) && regionIndices.sanitize (c) && shortCount <= regionIndices.len && - c->check_range (&StructAfter (regionIndices), + c->check_range (get_delta_bytes (), itemCount, get_row_size ())); } + bool serialize (hb_serialize_context_t *c, + const VarData *src, + const hb_map2_t &remap) + { + TRACE_SUBSET (this); + if (unlikely (!c->extend_min (*this))) return_trace (false); + itemCount.set (remap.get_count ()); + shortCount.set (src->shortCount); + + unsigned int row_size = src->get_row_size (); + if (unlikely (!c->allocate_size (src->regionIndices.get_size () + row_size * remap.get_count ()))) + return_trace (false); + + memcpy (®ionIndices, &src->regionIndices, src->regionIndices.get_size ()); + HBUINT8 *p = get_delta_bytes (); + for (unsigned int i = 0; i < remap.get_count (); i++) + { + memcpy (p, src->get_delta_bytes () + remap.new_to_old (i) * row_size, row_size); + p += row_size; + } + + return_trace (true); + } + + protected: + unsigned int get_row_size () const + { return shortCount + regionIndices.len; } + + const HBUINT8 *get_delta_bytes () const + { return &StructAfter (regionIndices); } + + HBUINT8 *get_delta_bytes () + { return &StructAfter (regionIndices); } + protected: HBUINT16 itemCount; HBUINT16 shortCount; @@ -1778,6 +1894,33 @@ struct VariationStore dataSets.sanitize (c, this)); } + bool serialize (hb_serialize_context_t *c, + const VariationStore *src, + const hb_array_t &inner_remaps) + { + TRACE_SUBSET (this); + if (unlikely (!c->extend_min (*this))) return_trace (false); + format.set (1); + if (unlikely (!regions.serialize (c, this) + .serialize (c, &(src+src->regions)))) return_trace (false); + + /* TODO: The following code could be simplified when + * OffsetListOf::subset () can take a custom param to be passed to VarData::serialize () + */ + dataSets.len.set (inner_remaps.length); + if (unlikely (!c->allocate_size (inner_remaps.length))) + return_trace (false); + + for (unsigned int i = 0; i < inner_remaps.length; i++) + { + if (unlikely (!dataSets[i].serialize (c, this) + .serialize (c, &(src+src->dataSets[i]), inner_remaps[i]))) + return_trace (false); + } + + return_trace (true); + } + unsigned int get_region_index_count (unsigned int ivs) const { return (this+dataSets[ivs]).get_region_index_count (); } @@ -1790,6 +1933,10 @@ struct VariationStore &scalars[0], num_scalars); } + const VarRegionList &get_regions () const { return this+regions; } + + unsigned int get_sub_table_count () const { return dataSets.len; } + protected: HBUINT16 format; LOffsetTo regions; @@ -2171,7 +2318,6 @@ struct Device DEFINE_SIZE_UNION (6, b); }; - } /* namespace OT */ diff --git a/src/hb-ot-var-hvar-table.hh b/src/hb-ot-var-hvar-table.hh index a8d9fe3c8..76378ad01 100644 --- a/src/hb-ot-var-hvar-table.hh +++ b/src/hb-ot-var-hvar-table.hh @@ -44,6 +44,36 @@ struct DeltaSetIndexMap get_width ())); } + bool serialize (hb_serialize_context_t *c, + unsigned int inner_bit_count, + unsigned int width, + const hb_array_t maps) + { + TRACE_SERIALIZE (this); + if (unlikely (maps.length && ((((inner_bit_count-1)&~0xF)!=0) || (((width-1)&~0x3)!=0)))) + return_trace (false); + if (unlikely (!c->extend_min (*this))) return_trace (false); + + format.set (((width-1)<<4)|(inner_bit_count-1)); + mapCount.set (maps.length); + HBUINT8 *p = c->allocate_size (width * maps.get_size ()); + if (unlikely (!p)) return_trace (false); + for (unsigned int i = 0; i < maps.length; i++) + { + unsigned int v = maps[i]; + unsigned int outer = v >> 16; + unsigned int inner = v & 0xFFFF; + unsigned int u = (outer << inner_bit_count)|inner; + for (unsigned int w = width; w > 0;) + { + p[--w].set (u); + u >>= 8; + } + p += width; + } + return_trace (true); + } + unsigned int map (unsigned int v) const /* Returns 16.16 outer.inner. */ { /* If count is zero, pass value unchanged. This takes @@ -72,7 +102,8 @@ struct DeltaSetIndexMap return u; } - protected: + unsigned int get_map_count () const { return mapCount; } + unsigned int get_width () const { return ((format >> 4) & 3) + 1; } unsigned int get_inner_bitcount () const { return (format & 0xF) + 1; } @@ -88,6 +119,143 @@ struct DeltaSetIndexMap DEFINE_SIZE_ARRAY (4, mapDataZ); }; +struct index_map_subset_plan_t +{ + index_map_subset_plan_t (void) : map_count (0), outer_bit_count (0), inner_bit_count (0) {} + ~index_map_subset_plan_t (void) { fini (); } + + void init (const DeltaSetIndexMap &index_map, + hb_vector_t &inner_remaps, + const hb_subset_plan_t *plan) + { + /* Identity map */ + if (&index_map == &Null(DeltaSetIndexMap)) + return; + + unsigned int last_map = (unsigned int)-1; + hb_codepoint_t last_gid = (hb_codepoint_t)-1; + hb_codepoint_t i = (hb_codepoint_t)index_map.get_map_count (); + + outer_bit_count = (index_map.get_width () * 8) - index_map.get_inner_bitcount (); + max_inners.resize (inner_remaps.length); + for (i = 0; i < inner_remaps.length; i++) max_inners[i] = 0; + + /* Search backwards for a map value different from the last map value */ + for (; i > 0; i--) + { + hb_codepoint_t old_gid; + if (!plan->old_gid_for_new_gid (i - 1, &old_gid)) + continue; + + unsigned int v = index_map.map (old_gid); + if (last_gid == (hb_codepoint_t)-1) + { + last_map = v; + last_gid = i; + continue; + } + if (v != last_map) break; + + last_map = i; + } + + map_count = last_map + 1; + for (unsigned int i = 0; i < map_count; i++) + { + hb_codepoint_t old_gid; + if (!plan->old_gid_for_new_gid (i, &old_gid)) + continue; + unsigned int v = index_map.map (old_gid); + unsigned int outer = v >> 16; + unsigned int inner = v & 0xFFFF; + if (inner > max_inners[outer]) max_inners[outer] = inner; + inner_remaps[outer].add (inner); + } + } + + void fini (void) {} + + void remap (const DeltaSetIndexMap *input_map, + const hb_vector_t &inner_remaps, + hb_vector_t & output_map) + { + for (unsigned int i = 0; i < max_inners.length; i++) + { + unsigned int bit_count = hb_bit_storage (inner_remaps[i][max_inners[i]]); + if (bit_count > inner_bit_count) inner_bit_count = bit_count; + } + + output_map.resize (map_count); + for (unsigned int i = 0; i < output_map.length; i++) + { + unsigned int v = input_map->map (i); + unsigned int outer = v >> 16; + output_map[i] = (outer << 16) | (inner_remaps[outer][v & 0xFFFF]); + } + } + + unsigned int get_inner_bitcount (void) const { return inner_bit_count; } + unsigned int get_width (void) const { return ((outer_bit_count + inner_bit_count + 7) / 8); } + unsigned int get_map_count (void) const { return map_count; } + + unsigned int get_size (void) const + { return (map_count? (DeltaSetIndexMap::min_size + get_width () * map_count): 0); } + + protected: + unsigned int map_count; + hb_vector_t + max_inners; + unsigned int outer_bit_count; + unsigned int inner_bit_count; +}; + +struct hvarvvar_subset_plan_t +{ + hvarvvar_subset_plan_t() : inner_remaps (), index_map_plans () {} + ~hvarvvar_subset_plan_t() { fini (); } + + void init (const hb_array_t &index_maps, + const VariationStore &_var_store, + const hb_subset_plan_t *plan) + { + index_map_plans.resize (index_maps.length); + var_store = &_var_store; + inner_remaps.resize (var_store->get_sub_table_count ()); + + for (unsigned int i = 0; i < inner_remaps.length; i++) + inner_remaps[i].init (); + + for (unsigned int i = 0; i < index_maps.length; i++) + { + index_map_plans[i].init (*index_maps[i], inner_remaps, plan); + index_map_subsets[i].init (); + } + + for (unsigned int i = 0; i < inner_remaps.length; i++) + { + if (inner_remaps[i].get_count () > 0) inner_remaps[i].reorder (); + } + + for (unsigned int i = 0; i < index_maps.length; i++) + { + index_map_plans[i].remap (index_maps[i], inner_remaps, index_map_subsets[i]); + } + } + + void fini (void) + { + inner_remaps.fini_deep (); + index_map_plans.fini_deep (); + index_map_subsets.fini_deep (); + } + + hb_vector_t inner_remaps; + hb_vector_t + index_map_plans; + hb_vector_t< hb_vector_t > + index_map_subsets; + const VariationStore *var_store; +}; /* * HVAR -- Horizontal Metrics Variations @@ -103,6 +271,14 @@ struct HVARVVAR static constexpr hb_tag_t HVARTag = HB_OT_TAG_HVAR; static constexpr hb_tag_t VVARTag = HB_OT_TAG_VVAR; + enum index_map_index_t { + ADV_INDEX, + LSB_INDEX, + RSB_INDEX, + TSB_INDEX, + VORG_INDEX + }; + bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); @@ -114,6 +290,57 @@ struct HVARVVAR rsbMap.sanitize (c, this)); } + bool serialize_index_maps (hb_serialize_context_t *c, + const hb_array_t &im_plans, + const hb_array_t > &im_subsets) + { + TRACE_SUBSET (this); + if (unlikely (!advMap.serialize (c, this) + .serialize (c, im_plans[ADV_INDEX].get_inner_bitcount (), + im_plans[ADV_INDEX].get_width (), + im_subsets[ADV_INDEX].as_array ()))) + return_trace (false); + if (unlikely (!lsbMap.serialize (c, this) + .serialize (c, im_plans[LSB_INDEX].get_inner_bitcount (), + im_plans[LSB_INDEX].get_width (), + im_subsets[LSB_INDEX].as_array ()))) + return_trace (false); + if (unlikely (!rsbMap.serialize (c, this) + .serialize (c, im_plans[RSB_INDEX].get_inner_bitcount (), + im_plans[RSB_INDEX].get_width (), + im_subsets[RSB_INDEX].as_array ()))) + return_trace (false); + return_trace (true); + } + + template + bool _subset (hb_subset_context_t *c) const + { + TRACE_SUBSET (this); + hvarvvar_subset_plan_t hvar_plan; + hb_vector_t + index_maps; + + index_maps.push (&(this+advMap)); + index_maps.push (&(this+lsbMap)); + index_maps.push (&(this+rsbMap)); + hvar_plan.init (index_maps.as_array (), this+varStore, c->plan); + + T *out = c->serializer->embed (*(T*)this); + if (unlikely (!out)) return_trace (false); + + out->version.major.set (1); + out->version.minor.set (0); + + if (!unlikely (out->varStore.serialize (c->serializer, this) + .serialize (c->serializer, hvar_plan.var_store, hvar_plan.inner_remaps.as_array ()))) + return_trace (false); + + return_trace (out->T::serialize_index_maps (c->serializer, + hvar_plan.index_map_plans.as_array (), + hvar_plan.index_map_subsets.as_array ())); + } + float get_advance_var (hb_codepoint_t glyph, const int *coords, unsigned int coord_count) const { @@ -141,6 +368,7 @@ struct HVARVVAR struct HVAR : HVARVVAR { static constexpr hb_tag_t tableTag = HB_OT_TAG_HVAR; + bool subset (hb_subset_context_t *c) const { return HVARVVAR::_subset (c); } }; struct VVAR : HVARVVAR { static constexpr hb_tag_t tableTag = HB_OT_TAG_VVAR; @@ -152,6 +380,8 @@ struct VVAR : HVARVVAR { vorgMap.sanitize (c, this)); } + bool subset (hb_subset_context_t *c) const { return HVARVVAR::_subset (c); } + protected: LOffsetTo vorgMap; /* Offset to vertical-origin var-idx mapping. */ diff --git a/src/hb-subset-cff-common.cc b/src/hb-subset-cff-common.cc index f29937a22..a7e05fe03 100644 --- a/src/hb-subset-cff-common.cc +++ b/src/hb-subset-cff-common.cc @@ -50,7 +50,7 @@ hb_plan_subset_cff_fdselect (const hb_subset_plan_t *plan, unsigned int &subset_fdselect_size /* OUT */, unsigned int &subset_fdselect_format /* OUT */, hb_vector_t &fdselect_ranges /* OUT */, - remap_t &fdmap /* OUT */) + hb_map2_t &fdmap /* OUT */) { subset_fd_count = 0; subset_fdselect_size = 0; @@ -97,13 +97,6 @@ hb_plan_subset_cff_fdselect (const hb_subset_plan_t *plan, } else { - /* create a fdmap */ - if (!fdmap.reset (fdCount)) - { - hb_set_destroy (set); - return false; - } - hb_codepoint_t fd = CFF_UNDEF_CODE; while (set->next (&fd)) fdmap.add (fd); diff --git a/src/hb-subset-cff-common.hh b/src/hb-subset-cff-common.hh index 81368ee4c..31280d594 100644 --- a/src/hb-subset-cff-common.hh +++ b/src/hb-subset-cff-common.hh @@ -541,19 +541,18 @@ struct subr_subset_param_t bool drop_hints; }; -struct subr_remap_t : remap_t +struct subr_remap_t : hb_map2_t { void create (hb_set_t *closure) { /* create a remapping of subroutine numbers from old to new. * no optimization based on usage counts. fonttools doesn't appear doing that either. */ - reset (closure->get_max () + 1); - for (hb_codepoint_t old_num = 0; old_num < length; old_num++) - { - if (hb_set_has (closure, old_num)) - add (old_num); - } + hb_codepoint_t max = closure->get_max (); + if (max != HB_MAP_VALUE_INVALID) + for (hb_codepoint_t old_num = 0; old_num <= max; old_num++) + if (closure->has (old_num)) + add (old_num); if (get_count () < 1240) bias = 107; @@ -563,14 +562,6 @@ struct subr_remap_t : remap_t bias = 32768; } - hb_codepoint_t operator[] (unsigned int old_num) const - { - if (old_num >= length) - return CFF_UNDEF_CODE; - else - return remap_t::operator[] (old_num); - } - int biased_num (unsigned int old_num) const { hb_codepoint_t new_num = (*this)[old_num]; @@ -687,8 +678,8 @@ struct subr_subsetter_t if (unlikely (!interp.interpret (param))) return false; - /* finalize parsed string esp. copy CFF1 width or CFF2 vsindex to the parsed charstring for encoding */ - SUBSETTER::finalize_parsed_str (interp.env, param, parsed_charstrings[i]); + /* complete parsed string esp. copy CFF1 width or CFF2 vsindex to the parsed charstring for encoding */ + SUBSETTER::complete_parsed_str (interp.env, param, parsed_charstrings[i]); } if (plan->drop_hints) @@ -1021,7 +1012,7 @@ hb_plan_subset_cff_fdselect (const hb_subset_plan_t *plan, unsigned int &subset_fdselect_size /* OUT */, unsigned int &subset_fdselect_format /* OUT */, hb_vector_t &fdselect_ranges /* OUT */, - CFF::remap_t &fdmap /* OUT */); + CFF::hb_map2_t &fdmap /* OUT */); HB_INTERNAL bool hb_serialize_cff_fdselect (hb_serialize_context_t *c, diff --git a/src/hb-subset-cff1.cc b/src/hb-subset-cff1.cc index 49ac0bf42..05ac471ee 100644 --- a/src/hb-subset-cff1.cc +++ b/src/hb-subset-cff1.cc @@ -34,12 +34,12 @@ using namespace CFF; -struct remap_sid_t : remap_t +struct remap_sid_t : hb_map2_t { unsigned int add (unsigned int sid) { if ((sid != CFF_UNDEF_SID) && !is_std_std (sid)) - return offset_sid (remap_t::add (unoffset_sid (sid))); + return offset_sid (hb_map2_t::add (unoffset_sid (sid))); else return sid; } @@ -49,7 +49,7 @@ struct remap_sid_t : remap_t if (is_std_std (sid) || (sid == CFF_UNDEF_SID)) return sid; else - return offset_sid (remap_t::operator [] (unoffset_sid (sid))); + return offset_sid (hb_map2_t::operator [] (unoffset_sid (sid))); } static const unsigned int num_std_strings = 391; @@ -326,7 +326,7 @@ struct cff1_cs_opset_flatten_t : cff1_cs_opset_t { /* replace the first glyph ID in the "glyph" field each range with a nLeft value */ - bool finalize (unsigned int last_glyph) + bool complete (unsigned int last_glyph) { bool two_byte = false; for (unsigned int i = (*this).length; i > 0; i--) @@ -397,7 +397,7 @@ struct cff1_subr_subsetter_t : subr_subsetter_tnum_output_glyphs () - 1); if (!two_byte) @@ -577,9 +577,6 @@ struct cff_subset_plan { bool collect_sids_in_dicts (const OT::cff1::accelerator_subset_t &acc) { - if (unlikely (!sidmap.reset (acc.stringIndex->count))) - return false; - for (unsigned int i = 0; i < name_dict_values_t::ValCount; i++) { unsigned int sid = acc.topDict.nameSIDs[i]; @@ -592,7 +589,7 @@ struct cff_subset_plan { if (acc.fdArray != &Null(CFF1FDArray)) for (unsigned int i = 0; i < orig_fdcount; i++) - if (fdmap.includes (i)) + if (fdmap.has (i)) (void)sidmap.add (acc.fontDicts[i].fontName); return true; @@ -735,7 +732,7 @@ struct cff_subset_plan { { subset_localsubrs[fd].init (); offsets.localSubrsInfos[fd].init (); - if (fdmap.includes (fd)) + if (fdmap.has (fd)) { if (!subr_subsetter.encode_localsubrs (fd, subset_localsubrs[fd])) return false; @@ -786,7 +783,7 @@ struct cff_subset_plan { cff1_font_dict_op_serializer_t fontSzr; unsigned int dictsSize = 0; for (unsigned int i = 0; i < acc.fontDicts.length; i++) - if (fdmap.includes (i)) + if (fdmap.has (i)) dictsSize += FontDict::calculate_serialized_size (acc.fontDicts[i], fontSzr); offsets.FDArrayInfo.offSize = calcOffSize (dictsSize); @@ -809,7 +806,7 @@ struct cff_subset_plan { offsets.privateDictInfo.offset = final_size; for (unsigned int i = 0; i < orig_fdcount; i++) { - if (fdmap.includes (i)) + if (fdmap.has (i)) { bool has_localsubrs = offsets.localSubrsInfos[i].size > 0; cff_private_dict_op_serializer_t privSzr (desubroutinize, plan->drop_hints); @@ -853,7 +850,7 @@ struct cff_subset_plan { /* font dict index remap table from fullset FDArray to subset FDArray. * set to CFF_UNDEF_CODE if excluded from subset */ - remap_t fdmap; + hb_map2_t fdmap; str_buff_vec_t subset_charstrings; str_buff_vec_t subset_globalsubrs; @@ -1030,7 +1027,7 @@ static inline bool _write_cff1 (const cff_subset_plan &plan, assert (plan.offsets.privateDictInfo.offset == (unsigned) (c.head - c.start)); for (unsigned int i = 0; i < acc.privateDicts.length; i++) { - if (plan.fdmap.includes (i)) + if (plan.fdmap.has (i)) { PrivateDict *pd = c.start_embed (); if (unlikely (pd == nullptr)) return false; diff --git a/src/hb-subset-cff2.cc b/src/hb-subset-cff2.cc index bf76a3ede..1a160d489 100644 --- a/src/hb-subset-cff2.cc +++ b/src/hb-subset-cff2.cc @@ -228,7 +228,7 @@ struct cff2_subr_subsetter_t : subr_subsetter_t 0; cff_private_dict_op_serializer_t privSzr (desubroutinize, drop_hints); @@ -427,7 +427,7 @@ struct cff2_subset_plan { unsigned int subset_fdselect_format; hb_vector_t subset_fdselect_ranges; - remap_t fdmap; + hb_map2_t fdmap; str_buff_vec_t subset_charstrings; str_buff_vec_t subset_globalsubrs; @@ -537,7 +537,7 @@ static inline bool _write_cff2 (const cff2_subset_plan &plan, assert (plan.offsets.privateDictsOffset == (unsigned) (c.head - c.start)); for (unsigned int i = 0; i < acc.privateDicts.length; i++) { - if (plan.fdmap.includes (i)) + if (plan.fdmap.has (i)) { PrivateDict *pd = c.start_embed (); if (unlikely (pd == nullptr)) return false; diff --git a/src/hb-subset.cc b/src/hb-subset.cc index 135265fc1..fa6e4d9f3 100644 --- a/src/hb-subset.cc +++ b/src/hb-subset.cc @@ -45,6 +45,7 @@ #include "hb-ot-vorg-table.hh" #include "hb-ot-layout-gsub-table.hh" #include "hb-ot-layout-gpos-table.hh" +#include "hb-ot-var-hvar-table.hh" static unsigned int @@ -198,6 +199,9 @@ _subset_table (hb_subset_plan_t *plan, case HB_OT_TAG_GPOS: result = _subset2 (plan); break; + case HB_OT_TAG_HVAR: + result = _subset2 (plan); + break; default: hb_blob_t *source_table = hb_face_reference_table (plan->source, tag); From a190140fa47ff5655edbb4eb414175852f722f85 Mon Sep 17 00:00:00 2001 From: Michiharu Ariza Date: Thu, 21 Feb 2019 15:47:27 -0800 Subject: [PATCH 02/16] fix HVAR & VarStore subsetting --- src/hb-ot-layout-common.hh | 15 ++-- src/hb-ot-var-hvar-table.hh | 140 ++++++++++++++++++------------------ 2 files changed, 77 insertions(+), 78 deletions(-) diff --git a/src/hb-ot-layout-common.hh b/src/hb-ot-layout-common.hh index c41865ff2..149a2d066 100644 --- a/src/hb-ot-layout-common.hh +++ b/src/hb-ot-layout-common.hh @@ -1741,8 +1741,9 @@ struct VarRegionList bool serialize (hb_serialize_context_t *c, const VarRegionList *src) { TRACE_SERIALIZE (this); - if (unlikely (!c->allocate_size (src->get_size ()))) return_trace (false); - memcpy (this, src, src->get_size ()); + unsigned int size = src->get_size (); + if (unlikely (!c->allocate_size (size))) return_trace (false); + memcpy (this, src, size); return_trace (true); } @@ -1831,14 +1832,14 @@ struct VarData shortCount.set (src->shortCount); unsigned int row_size = src->get_row_size (); - if (unlikely (!c->allocate_size (src->regionIndices.get_size () + row_size * remap.get_count ()))) + if (unlikely (!c->allocate_size (src->regionIndices.get_size () + (row_size * remap.get_count ())))) return_trace (false); memcpy (®ionIndices, &src->regionIndices, src->regionIndices.get_size ()); HBUINT8 *p = get_delta_bytes (); for (unsigned int i = 0; i < remap.get_count (); i++) { - memcpy (p, src->get_delta_bytes () + remap.new_to_old (i) * row_size, row_size); + memcpy (p, src->get_delta_bytes () + (row_size * remap.new_to_old (i)), row_size); p += row_size; } @@ -1899,7 +1900,8 @@ struct VariationStore const hb_array_t &inner_remaps) { TRACE_SUBSET (this); - if (unlikely (!c->extend_min (*this))) return_trace (false); + unsigned int size = min_size + HBUINT32::static_size * inner_remaps.length; + if (unlikely (!c->allocate_size (size))) return_trace (false); format.set (1); if (unlikely (!regions.serialize (c, this) .serialize (c, &(src+src->regions)))) return_trace (false); @@ -1908,9 +1910,6 @@ struct VariationStore * OffsetListOf::subset () can take a custom param to be passed to VarData::serialize () */ dataSets.len.set (inner_remaps.length); - if (unlikely (!c->allocate_size (inner_remaps.length))) - return_trace (false); - for (unsigned int i = 0; i < inner_remaps.length; i++) { if (unlikely (!dataSets[i].serialize (c, this) diff --git a/src/hb-ot-var-hvar-table.hh b/src/hb-ot-var-hvar-table.hh index 76378ad01..fa74a37a9 100644 --- a/src/hb-ot-var-hvar-table.hh +++ b/src/hb-ot-var-hvar-table.hh @@ -44,30 +44,32 @@ struct DeltaSetIndexMap get_width ())); } - bool serialize (hb_serialize_context_t *c, - unsigned int inner_bit_count, - unsigned int width, - const hb_array_t maps) + template + bool serialize (hb_serialize_context_t *c, const T &plan) { + unsigned int width = plan.get_width (); + unsigned int inner_bit_count = plan.get_inner_bit_count (); + auto map = plan.get_output_map (); + TRACE_SERIALIZE (this); - if (unlikely (maps.length && ((((inner_bit_count-1)&~0xF)!=0) || (((width-1)&~0x3)!=0)))) + if (unlikely (map.length && ((((inner_bit_count-1)&~0xF)!=0) || (((width-1)&~0x3)!=0)))) return_trace (false); if (unlikely (!c->extend_min (*this))) return_trace (false); format.set (((width-1)<<4)|(inner_bit_count-1)); - mapCount.set (maps.length); - HBUINT8 *p = c->allocate_size (width * maps.get_size ()); + mapCount.set (map.length); + HBUINT8 *p = c->allocate_size (width * map.length); if (unlikely (!p)) return_trace (false); - for (unsigned int i = 0; i < maps.length; i++) + for (unsigned int i = 0; i < map.length; i++) { - unsigned int v = maps[i]; + unsigned int v = map[i]; unsigned int outer = v >> 16; unsigned int inner = v & 0xFFFF; unsigned int u = (outer << inner_bit_count)|inner; for (unsigned int w = width; w > 0;) { - p[--w].set (u); - u >>= 8; + p[--w].set (u); + u >>= 8; } p += width; } @@ -93,7 +95,7 @@ struct DeltaSetIndexMap } { /* Repack it. */ - unsigned int n = get_inner_bitcount (); + unsigned int n = get_inner_bit_count (); unsigned int outer = u >> n; unsigned int inner = u & ((1 << n) - 1); u = (outer<<16) | inner; @@ -102,11 +104,9 @@ struct DeltaSetIndexMap return u; } - unsigned int get_map_count () const { return mapCount; } - - unsigned int get_width () const { return ((format >> 4) & 3) + 1; } - - unsigned int get_inner_bitcount () const { return (format & 0xF) + 1; } + unsigned int get_map_count () const { return mapCount; } + unsigned int get_width () const { return ((format >> 4) & 3) + 1; } + unsigned int get_inner_bit_count () const { return (format & 0xF) + 1; } protected: HBUINT16 format; /* A packed field that describes the compressed @@ -121,49 +121,52 @@ struct DeltaSetIndexMap struct index_map_subset_plan_t { - index_map_subset_plan_t (void) : map_count (0), outer_bit_count (0), inner_bit_count (0) {} - ~index_map_subset_plan_t (void) { fini (); } - void init (const DeltaSetIndexMap &index_map, hb_vector_t &inner_remaps, const hb_subset_plan_t *plan) { + map_count = 0; + outer_bit_count = 0; + inner_bit_count = 0; + max_inners.init (); + output_map.init (); + /* Identity map */ if (&index_map == &Null(DeltaSetIndexMap)) return; - unsigned int last_map = (unsigned int)-1; + unsigned int last_val = (unsigned int)-1; hb_codepoint_t last_gid = (hb_codepoint_t)-1; - hb_codepoint_t i = (hb_codepoint_t)index_map.get_map_count (); + hb_codepoint_t gid = (hb_codepoint_t)index_map.get_map_count (); - outer_bit_count = (index_map.get_width () * 8) - index_map.get_inner_bitcount (); + outer_bit_count = (index_map.get_width () * 8) - index_map.get_inner_bit_count (); max_inners.resize (inner_remaps.length); - for (i = 0; i < inner_remaps.length; i++) max_inners[i] = 0; + for (unsigned i = 0; i < inner_remaps.length; i++) max_inners[i] = 0; /* Search backwards for a map value different from the last map value */ - for (; i > 0; i--) + for (; gid > 0; gid--) { hb_codepoint_t old_gid; - if (!plan->old_gid_for_new_gid (i - 1, &old_gid)) + if (!plan->old_gid_for_new_gid (gid - 1, &old_gid)) continue; unsigned int v = index_map.map (old_gid); if (last_gid == (hb_codepoint_t)-1) { - last_map = v; - last_gid = i; + last_val = v; + last_gid = gid; continue; } - if (v != last_map) break; + if (v != last_val) break; - last_map = i; + last_gid = gid; } - map_count = last_map + 1; - for (unsigned int i = 0; i < map_count; i++) + map_count = last_gid + 1; + for (gid = 0; gid < map_count; gid++) { hb_codepoint_t old_gid; - if (!plan->old_gid_for_new_gid (i, &old_gid)) + if (!plan->old_gid_for_new_gid (gid, &old_gid)) continue; unsigned int v = index_map.map (old_gid); unsigned int outer = v >> 16; @@ -173,11 +176,15 @@ struct index_map_subset_plan_t } } - void fini (void) {} + void fini (void) + { + max_inners.fini (); + output_map.fini (); + } - void remap (const DeltaSetIndexMap *input_map, - const hb_vector_t &inner_remaps, - hb_vector_t & output_map) + void remap (const hb_subset_plan_t *plan, + const DeltaSetIndexMap *input_map, + const hb_vector_t &inner_remaps) { for (unsigned int i = 0; i < max_inners.length; i++) { @@ -186,27 +193,33 @@ struct index_map_subset_plan_t } output_map.resize (map_count); - for (unsigned int i = 0; i < output_map.length; i++) + for (hb_codepoint_t gid = 0; gid < output_map.length; gid++) { - unsigned int v = input_map->map (i); + hb_codepoint_t old_gid = 0; + (void)plan->old_gid_for_new_gid (gid, &old_gid); + unsigned int v = input_map->map (old_gid); unsigned int outer = v >> 16; - output_map[i] = (outer << 16) | (inner_remaps[outer][v & 0xFFFF]); + output_map[gid] = (outer << 16) | (inner_remaps[outer][v & 0xFFFF]); } } - unsigned int get_inner_bitcount (void) const { return inner_bit_count; } + unsigned int get_inner_bit_count (void) const { return inner_bit_count; } unsigned int get_width (void) const { return ((outer_bit_count + inner_bit_count + 7) / 8); } unsigned int get_map_count (void) const { return map_count; } unsigned int get_size (void) const { return (map_count? (DeltaSetIndexMap::min_size + get_width () * map_count): 0); } + hb_array_t get_output_map (void) const { return output_map.as_array (); } + protected: unsigned int map_count; hb_vector_t max_inners; unsigned int outer_bit_count; unsigned int inner_bit_count; + hb_vector_t + output_map; }; struct hvarvvar_subset_plan_t @@ -219,6 +232,7 @@ struct hvarvvar_subset_plan_t const hb_subset_plan_t *plan) { index_map_plans.resize (index_maps.length); + var_store = &_var_store; inner_remaps.resize (var_store->get_sub_table_count ()); @@ -226,34 +240,24 @@ struct hvarvvar_subset_plan_t inner_remaps[i].init (); for (unsigned int i = 0; i < index_maps.length; i++) - { index_map_plans[i].init (*index_maps[i], inner_remaps, plan); - index_map_subsets[i].init (); - } for (unsigned int i = 0; i < inner_remaps.length; i++) - { if (inner_remaps[i].get_count () > 0) inner_remaps[i].reorder (); - } for (unsigned int i = 0; i < index_maps.length; i++) - { - index_map_plans[i].remap (index_maps[i], inner_remaps, index_map_subsets[i]); - } + index_map_plans[i].remap (plan, index_maps[i], inner_remaps); } void fini (void) { inner_remaps.fini_deep (); index_map_plans.fini_deep (); - index_map_subsets.fini_deep (); } hb_vector_t inner_remaps; hb_vector_t index_map_plans; - hb_vector_t< hb_vector_t > - index_map_subsets; const VariationStore *var_store; }; @@ -291,25 +295,22 @@ struct HVARVVAR } bool serialize_index_maps (hb_serialize_context_t *c, - const hb_array_t &im_plans, - const hb_array_t > &im_subsets) + const hb_array_t &im_plans) { TRACE_SUBSET (this); - if (unlikely (!advMap.serialize (c, this) - .serialize (c, im_plans[ADV_INDEX].get_inner_bitcount (), - im_plans[ADV_INDEX].get_width (), - im_subsets[ADV_INDEX].as_array ()))) + if (im_plans[ADV_INDEX].get_map_count () == 0) + advMap.set (0); + else if (unlikely (!advMap.serialize (c, this).serialize (c, im_plans[ADV_INDEX]))) return_trace (false); - if (unlikely (!lsbMap.serialize (c, this) - .serialize (c, im_plans[LSB_INDEX].get_inner_bitcount (), - im_plans[LSB_INDEX].get_width (), - im_subsets[LSB_INDEX].as_array ()))) + if (im_plans[LSB_INDEX].get_map_count () == 0) + lsbMap.set (0); + else if (unlikely (!lsbMap.serialize (c, this).serialize (c, im_plans[LSB_INDEX]))) return_trace (false); - if (unlikely (!rsbMap.serialize (c, this) - .serialize (c, im_plans[RSB_INDEX].get_inner_bitcount (), - im_plans[RSB_INDEX].get_width (), - im_subsets[RSB_INDEX].as_array ()))) + if (im_plans[RSB_INDEX].get_map_count () == 0) + rsbMap.set (0); + else if (unlikely (!rsbMap.serialize (c, this).serialize (c, im_plans[RSB_INDEX]))) return_trace (false); + return_trace (true); } @@ -326,19 +327,18 @@ struct HVARVVAR index_maps.push (&(this+rsbMap)); hvar_plan.init (index_maps.as_array (), this+varStore, c->plan); - T *out = c->serializer->embed (*(T*)this); + T *out = c->serializer->allocate_min (); if (unlikely (!out)) return_trace (false); out->version.major.set (1); out->version.minor.set (0); - if (!unlikely (out->varStore.serialize (c->serializer, this) + if (!unlikely (out->varStore.serialize (c->serializer, out) .serialize (c->serializer, hvar_plan.var_store, hvar_plan.inner_remaps.as_array ()))) return_trace (false); return_trace (out->T::serialize_index_maps (c->serializer, - hvar_plan.index_map_plans.as_array (), - hvar_plan.index_map_subsets.as_array ())); + hvar_plan.index_map_plans.as_array ())); } float get_advance_var (hb_codepoint_t glyph, From a7b801f6d1410443f518cec5dad4d89a8feaf01d Mon Sep 17 00:00:00 2001 From: Michiharu Ariza Date: Thu, 21 Feb 2019 16:34:49 -0800 Subject: [PATCH 03/16] fix CFF2 local subr subsetting bug exposed by impl change of fdmap also fixed name of subr_remap_ts as subr_remaps_t --- src/hb-subset-cff-common.hh | 8 ++++---- src/hb-subset-cff2.cc | 20 +++++++++----------- 2 files changed, 13 insertions(+), 15 deletions(-) diff --git a/src/hb-subset-cff-common.hh b/src/hb-subset-cff-common.hh index 31280d594..2694e0c56 100644 --- a/src/hb-subset-cff-common.hh +++ b/src/hb-subset-cff-common.hh @@ -572,15 +572,15 @@ struct subr_remap_t : hb_map2_t int bias; }; -struct subr_remap_ts +struct subr_remaps_t { - subr_remap_ts () + subr_remaps_t () { global_remap.init (); local_remaps.init (); } - ~subr_remap_ts () { fini (); } + ~subr_remaps_t () { fini (); } void init (unsigned int fdCount) { @@ -996,7 +996,7 @@ struct subr_subsetter_t parsed_cs_str_vec_t parsed_global_subrs; hb_vector_t parsed_local_subrs; - subr_remap_ts remaps; + subr_remaps_t remaps; private: typedef typename SUBRS::count_type subr_count_type; diff --git a/src/hb-subset-cff2.cc b/src/hb-subset-cff2.cc index 1a160d489..1211a166a 100644 --- a/src/hb-subset-cff2.cc +++ b/src/hb-subset-cff2.cc @@ -326,18 +326,16 @@ struct cff2_subset_plan { { subset_localsubrs[fd].init (); offsets.localSubrsInfos[fd].init (); - if (fdmap.has (fd)) - { - if (!subr_subsetter.encode_localsubrs (fd, subset_localsubrs[fd])) - return false; - unsigned int dataSize = subset_localsubrs[fd].total_size (); - if (dataSize > 0) - { - offsets.localSubrsInfos[fd].offset = final_size; - offsets.localSubrsInfos[fd].offSize = calcOffSize (dataSize); - offsets.localSubrsInfos[fd].size = CFF2Subrs::calculate_serialized_size (offsets.localSubrsInfos[fd].offSize, subset_localsubrs[fd].length, dataSize); - } + if (!subr_subsetter.encode_localsubrs (fd, subset_localsubrs[fd])) + return false; + + unsigned int dataSize = subset_localsubrs[fd].total_size (); + if (dataSize > 0) + { + offsets.localSubrsInfos[fd].offset = final_size; + offsets.localSubrsInfos[fd].offSize = calcOffSize (dataSize); + offsets.localSubrsInfos[fd].size = CFF2Subrs::calculate_serialized_size (offsets.localSubrsInfos[fd].offSize, subset_localsubrs[fd].length, dataSize); } } } From ca3b4a21dff4fcd804b0bf2249fb3f286486d8fa Mon Sep 17 00:00:00 2001 From: Michiharu Ariza Date: Thu, 21 Feb 2019 16:42:30 -0800 Subject: [PATCH 04/16] fixed compiler gripes --- src/hb-ot-var-hvar-table.hh | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/hb-ot-var-hvar-table.hh b/src/hb-ot-var-hvar-table.hh index fa74a37a9..7af2b8a2e 100644 --- a/src/hb-ot-var-hvar-table.hh +++ b/src/hb-ot-var-hvar-table.hh @@ -49,20 +49,20 @@ struct DeltaSetIndexMap { unsigned int width = plan.get_width (); unsigned int inner_bit_count = plan.get_inner_bit_count (); - auto map = plan.get_output_map (); + const hb_array_t output_map = plan.get_output_map (); TRACE_SERIALIZE (this); - if (unlikely (map.length && ((((inner_bit_count-1)&~0xF)!=0) || (((width-1)&~0x3)!=0)))) + if (unlikely (output_map.length && ((((inner_bit_count-1)&~0xF)!=0) || (((width-1)&~0x3)!=0)))) return_trace (false); if (unlikely (!c->extend_min (*this))) return_trace (false); format.set (((width-1)<<4)|(inner_bit_count-1)); - mapCount.set (map.length); - HBUINT8 *p = c->allocate_size (width * map.length); + mapCount.set (output_map.length); + HBUINT8 *p = c->allocate_size (width * output_map.length); if (unlikely (!p)) return_trace (false); - for (unsigned int i = 0; i < map.length; i++) + for (unsigned int i = 0; i < output_map.length; i++) { - unsigned int v = map[i]; + unsigned int v = output_map[i]; unsigned int outer = v >> 16; unsigned int inner = v & 0xFFFF; unsigned int u = (outer << inner_bit_count)|inner; From 2d545e1e86d94af05550118cac20ec097bc843b4 Mon Sep 17 00:00:00 2001 From: Michiharu Ariza Date: Fri, 22 Feb 2019 10:22:08 -0800 Subject: [PATCH 05/16] unuse set in hb_map2_t impl also some code cleanup --- src/hb-ot-layout-common.hh | 46 +++++++++++++++++++------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/src/hb-ot-layout-common.hh b/src/hb-ot-layout-common.hh index 149a2d066..a6e8e3235 100644 --- a/src/hb-ot-layout-common.hh +++ b/src/hb-ot-layout-common.hh @@ -1586,6 +1586,9 @@ static inline void ClassDef_serialize (hb_serialize_context_t *c, hb_array_t klasses) { c->start_embed ()->serialize (c, glyphs, klasses); } +/* Bi-directional map. + * nww ids are assigned incrementally & contiguous; old ids may be random & sparse + * all mappings unique & no duplicate */ struct hb_map2_t { hb_map2_t () { init (); } @@ -1596,71 +1599,68 @@ struct hb_map2_t count = 0; old_to_new_map.init (); new_to_old_map.init (); - set.init (); } void fini (void) { old_to_new_map.fini (); new_to_old_map.fini (); - set.fini (); } - bool has (hb_codepoint_t id) const { return set.has (id); } + bool has (hb_codepoint_t _old) const { return old_to_new_map.has (_old); } - hb_codepoint_t add (hb_codepoint_t i) + hb_codepoint_t add (hb_codepoint_t _old) { - hb_codepoint_t v = old_to_new_map[i]; - if (v == HB_MAP_VALUE_INVALID) + hb_codepoint_t _new = old_to_new_map[_old]; + if (_new == HB_MAP_VALUE_INVALID) { - set.add (i); - v = count++; - old_to_new_map.set (i, v); - new_to_old_map.set (v, i); + _new = count++; + old_to_new_map.set (_old, _new); + new_to_old_map.set (_new, _old); } - return v; + return _new; } /* returns HB_MAP_VALUE_INVALID if unmapped */ - hb_codepoint_t operator [] (hb_codepoint_t i) const { return old_to_new (i); } - hb_codepoint_t old_to_new (hb_codepoint_t i) const { return old_to_new_map[i]; } - hb_codepoint_t new_to_old (hb_codepoint_t i) const { return new_to_old_map[i]; } + hb_codepoint_t operator [] (hb_codepoint_t _old) const { return old_to_new (_old); } + hb_codepoint_t old_to_new (hb_codepoint_t _old) const { return old_to_new_map[_old]; } + hb_codepoint_t new_to_old (hb_codepoint_t _new) const { return new_to_old_map[_new]; } bool identity (unsigned int size) { hb_codepoint_t i; old_to_new_map.clear (); new_to_old_map.clear (); - set.clear (); for (i = 0; i < size; i++) { old_to_new_map.set (i, i); new_to_old_map.set (i, i); - set.add (i); } count = i; - return old_to_new_map.successful && new_to_old_map.successful && set.successful; + return old_to_new_map.successful && new_to_old_map.successful; } /* Optional: after finished adding all mappings in a random order, - * reorder outputs in the same order as the inputs. */ + * reassign new ids to old ids so that they are in the same order. */ void reorder (void) { - for (hb_codepoint_t i = HB_SET_VALUE_INVALID, count = 0; set.next (&i); count++) + hb_codepoint_t _new = 0; + for (hb_codepoint_t _old = 0; _new < count; _old++) { - new_to_old_map.set (count, i); - old_to_new_map.set (i, count); + if (!has (_old)) continue; + new_to_old_map.set (_new, _old); + old_to_new_map.set (_old, _new); + _old++; + _new++; } } unsigned int get_count () const { return count; } - unsigned int get_bits () const { return count? hb_bit_storage (count - 1): 0; } protected: unsigned int count; hb_map_t old_to_new_map; hb_map_t new_to_old_map; - hb_set_t set; }; /* From 5d781f62ba4df4f322b01ded5008d1c936acd585 Mon Sep 17 00:00:00 2001 From: Michiharu Ariza Date: Fri, 22 Feb 2019 14:53:06 -0800 Subject: [PATCH 06/16] subset VVAR & outer indices --- src/hb-ot-var-hvar-table.hh | 49 ++++++++++++++++++++++++++++++------- src/hb-subset.cc | 3 +++ 2 files changed, 43 insertions(+), 9 deletions(-) diff --git a/src/hb-ot-var-hvar-table.hh b/src/hb-ot-var-hvar-table.hh index 7af2b8a2e..382da7aff 100644 --- a/src/hb-ot-var-hvar-table.hh +++ b/src/hb-ot-var-hvar-table.hh @@ -122,6 +122,7 @@ struct DeltaSetIndexMap struct index_map_subset_plan_t { void init (const DeltaSetIndexMap &index_map, + hb_map2_t &outer_remap, hb_vector_t &inner_remaps, const hb_subset_plan_t *plan) { @@ -171,6 +172,7 @@ struct index_map_subset_plan_t unsigned int v = index_map.map (old_gid); unsigned int outer = v >> 16; unsigned int inner = v & 0xFFFF; + outer_remap.add (outer); if (inner > max_inners[outer]) max_inners[outer] = inner; inner_remaps[outer].add (inner); } @@ -184,10 +186,12 @@ struct index_map_subset_plan_t void remap (const hb_subset_plan_t *plan, const DeltaSetIndexMap *input_map, + const hb_map2_t &outer_remap, const hb_vector_t &inner_remaps) { for (unsigned int i = 0; i < max_inners.length; i++) { + if (inner_remaps[i].get_count () == 0) continue; unsigned int bit_count = hb_bit_storage (inner_remaps[i][max_inners[i]]); if (bit_count > inner_bit_count) inner_bit_count = bit_count; } @@ -199,7 +203,7 @@ struct index_map_subset_plan_t (void)plan->old_gid_for_new_gid (gid, &old_gid); unsigned int v = input_map->map (old_gid); unsigned int outer = v >> 16; - output_map[gid] = (outer << 16) | (inner_remaps[outer][v & 0xFFFF]); + output_map[gid] = (outer_remap[outer] << 16) | (inner_remaps[outer][v & 0xFFFF]); } } @@ -240,13 +244,14 @@ struct hvarvvar_subset_plan_t inner_remaps[i].init (); for (unsigned int i = 0; i < index_maps.length; i++) - index_map_plans[i].init (*index_maps[i], inner_remaps, plan); + index_map_plans[i].init (*index_maps[i], outer_remap, inner_remaps, plan); + outer_remap.reorder (); for (unsigned int i = 0; i < inner_remaps.length; i++) if (inner_remaps[i].get_count () > 0) inner_remaps[i].reorder (); for (unsigned int i = 0; i < index_maps.length; i++) - index_map_plans[i].remap (plan, index_maps[i], inner_remaps); + index_map_plans[i].remap (plan, index_maps[i], outer_remap, inner_remaps); } void fini (void) @@ -255,6 +260,7 @@ struct hvarvvar_subset_plan_t index_map_plans.fini_deep (); } + hb_map2_t outer_remap; hb_vector_t inner_remaps; hb_vector_t index_map_plans; @@ -294,19 +300,26 @@ struct HVARVVAR rsbMap.sanitize (c, this)); } + void listup_index_maps (hb_vector_t &index_maps) const + { + index_maps.push (&(this+advMap)); + index_maps.push (&(this+lsbMap)); + index_maps.push (&(this+rsbMap)); + } + bool serialize_index_maps (hb_serialize_context_t *c, const hb_array_t &im_plans) { TRACE_SUBSET (this); - if (im_plans[ADV_INDEX].get_map_count () == 0) + if (!im_plans[ADV_INDEX].get_map_count ()) advMap.set (0); else if (unlikely (!advMap.serialize (c, this).serialize (c, im_plans[ADV_INDEX]))) return_trace (false); - if (im_plans[LSB_INDEX].get_map_count () == 0) + if (!im_plans[LSB_INDEX].get_map_count ()) lsbMap.set (0); else if (unlikely (!lsbMap.serialize (c, this).serialize (c, im_plans[LSB_INDEX]))) return_trace (false); - if (im_plans[RSB_INDEX].get_map_count () == 0) + if (!im_plans[RSB_INDEX].get_map_count ()) rsbMap.set (0); else if (unlikely (!rsbMap.serialize (c, this).serialize (c, im_plans[RSB_INDEX]))) return_trace (false); @@ -322,9 +335,7 @@ struct HVARVVAR hb_vector_t index_maps; - index_maps.push (&(this+advMap)); - index_maps.push (&(this+lsbMap)); - index_maps.push (&(this+rsbMap)); + ((T*)this)->listup_index_maps (index_maps); hvar_plan.init (index_maps.as_array (), this+varStore, c->plan); T *out = c->serializer->allocate_min (); @@ -380,6 +391,26 @@ struct VVAR : HVARVVAR { vorgMap.sanitize (c, this)); } + void listup_index_maps (hb_vector_t &index_maps) const + { + HVARVVAR::listup_index_maps (index_maps); + index_maps.push (&(this+vorgMap)); + } + + bool serialize_index_maps (hb_serialize_context_t *c, + const hb_array_t &im_plans) + { + TRACE_SUBSET (this); + if (unlikely (!HVARVVAR::serialize_index_maps (c, im_plans))) + return_trace (false); + if (!im_plans[VORG_INDEX].get_map_count ()) + vorgMap.set (0); + else if (unlikely (!vorgMap.serialize (c, this).serialize (c, im_plans[VORG_INDEX]))) + return_trace (false); + + return_trace (true); + } + bool subset (hb_subset_context_t *c) const { return HVARVVAR::_subset (c); } protected: diff --git a/src/hb-subset.cc b/src/hb-subset.cc index fa6e4d9f3..4441575ab 100644 --- a/src/hb-subset.cc +++ b/src/hb-subset.cc @@ -202,6 +202,9 @@ _subset_table (hb_subset_plan_t *plan, case HB_OT_TAG_HVAR: result = _subset2 (plan); break; + case HB_OT_TAG_VVAR: + result = _subset2 (plan); + break; default: hb_blob_t *source_table = hb_face_reference_table (plan->source, tag); From d817b446a132816e48e9ce4e3619a52a340ab35a Mon Sep 17 00:00:00 2001 From: Michiharu Ariza Date: Mon, 25 Feb 2019 09:59:27 -0800 Subject: [PATCH 07/16] renamed hb_map2_t to hb_bimap_h in its own .hh --- src/Makefile.sources | 1 + src/hb-bimap.hh | 109 +++++++++++++++++++ src/hb-ot-cff-common.hh | 4 +- src/hb-ot-cff1-table.hh | 4 +- src/hb-ot-gvar-table.hh | 211 ++++++++++++++++++++++++++++++++++++ src/hb-ot-layout-common.hh | 84 +------------- src/hb-ot-var-hvar-table.hh | 12 +- src/hb-subset-cff-common.cc | 2 +- src/hb-subset-cff-common.hh | 4 +- src/hb-subset-cff1.cc | 8 +- src/hb-subset-cff2.cc | 2 +- 11 files changed, 343 insertions(+), 98 deletions(-) create mode 100644 src/hb-bimap.hh create mode 100644 src/hb-ot-gvar-table.hh diff --git a/src/Makefile.sources b/src/Makefile.sources index 0da4abe05..db818283f 100644 --- a/src/Makefile.sources +++ b/src/Makefile.sources @@ -41,6 +41,7 @@ HB_BASE_sources = \ hb-machinery.hh \ hb-map.cc \ hb-map.hh \ + hb-bimap.hh \ hb-mutex.hh \ hb-null.hh \ hb-object.hh \ diff --git a/src/hb-bimap.hh b/src/hb-bimap.hh new file mode 100644 index 000000000..1c8fa553c --- /dev/null +++ b/src/hb-bimap.hh @@ -0,0 +1,109 @@ +/* + * Copyright © 2019 Adobe 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. + * + * Adobe Author(s): Michiharu Ariza + */ + +#ifndef HB_BIMAP_HH +#define HB_BIMAP_HH + +#include "hb.hh" + +/* Bi-directional map. + * new ids are assigned incrementally & contiguously to old ids + * which may be added randomly & sparsely + * all mappings are 1-to-1 in both directions */ +struct hb_bimap_t +{ + hb_bimap_t () { init (); } + ~hb_bimap_t () { fini (); } + + void init (void) + { + count = 0; + old_to_new.init (); + new_to_old.init (); + } + + void fini (void) + { + old_to_new.fini (); + new_to_old.fini (); + } + + bool has (hb_codepoint_t _old) const { return old_to_new.has (_old); } + + hb_codepoint_t add (hb_codepoint_t _old) + { + hb_codepoint_t _new = old_to_new[_old]; + if (_new == HB_MAP_VALUE_INVALID) + { + _new = count++; + old_to_new.set (_old, _new); + new_to_old.resize (count); + new_to_old[_new] = _old; + } + return _new; + } + + /* returns HB_MAP_VALUE_INVALID if unmapped */ + hb_codepoint_t operator [] (hb_codepoint_t _old) const { return to_new (_old); } + hb_codepoint_t to_new (hb_codepoint_t _old) const { return old_to_new[_old]; } + hb_codepoint_t to_old (hb_codepoint_t _new) const { return (_new >= count)? HB_MAP_VALUE_INVALID: new_to_old[_new]; } + + bool identity (unsigned int size) + { + hb_codepoint_t i; + old_to_new.clear (); + new_to_old.resize (size); + for (i = 0; i < size; i++) + { + old_to_new.set (i, i); + new_to_old[i] = i; + } + count = i; + return old_to_new.successful && !new_to_old.in_error (); + } + + static int cmp_id (const void* a, const void* b) + { return (int)*(const hb_codepoint_t *)a - (int)*(const hb_codepoint_t *)b; } + + /* Optional: after finished adding all mappings in a random order, + * reassign new ids to old ids so that both are in the same order. */ + void reorder (void) + { + new_to_old.qsort (cmp_id); + for (hb_codepoint_t _new = 0; _new < count; _new++) + old_to_new.set (to_old (_new), _new); + } + + unsigned int get_count () const { return count; } + + protected: + unsigned int count; + hb_map_t old_to_new; + hb_vector_t + new_to_old; +}; + +#endif /* HB_BIMAP_HH */ diff --git a/src/hb-ot-cff-common.hh b/src/hb-ot-cff-common.hh index 5e0082d20..78e80b2fa 100644 --- a/src/hb-ot-cff-common.hh +++ b/src/hb-ot-cff-common.hh @@ -457,7 +457,7 @@ struct FDArray : CFFIndexOf unsigned int offSize_, const hb_vector_t &fontDicts, unsigned int fdCount, - const hb_map2_t &fdmap, + const hb_bimap_t &fdmap, OP_SERIALIZER& opszr, const hb_vector_t &privateInfos) { @@ -495,7 +495,7 @@ struct FDArray : CFFIndexOf static unsigned int calculate_serialized_size (unsigned int &offSize_ /* OUT */, const hb_vector_t &fontDicts, unsigned int fdCount, - const hb_map2_t &fdmap, + const hb_bimap_t &fdmap, OP_SERIALIZER& opszr) { unsigned int dictsSize = 0; diff --git a/src/hb-ot-cff1-table.hh b/src/hb-ot-cff1-table.hh index d65b2250a..2f502a7d2 100644 --- a/src/hb-ot-cff1-table.hh +++ b/src/hb-ot-cff1-table.hh @@ -570,7 +570,7 @@ struct Charset { struct CFF1StringIndex : CFF1Index { bool serialize (hb_serialize_context_t *c, const CFF1StringIndex &strings, - unsigned int offSize_, const hb_map2_t &sidmap) + unsigned int offSize_, const hb_bimap_t &sidmap) { TRACE_SERIALIZE (this); if (unlikely ((strings.count == 0) || (sidmap.get_count () == 0))) @@ -598,7 +598,7 @@ struct CFF1StringIndex : CFF1Index } /* in parallel to above */ - unsigned int calculate_serialized_size (unsigned int &offSize /*OUT*/, const hb_map2_t &sidmap) const + unsigned int calculate_serialized_size (unsigned int &offSize /*OUT*/, const hb_bimap_t &sidmap) const { offSize = 0; if ((count == 0) || (sidmap.get_count () == 0)) diff --git a/src/hb-ot-gvar-table.hh b/src/hb-ot-gvar-table.hh new file mode 100644 index 000000000..f0077150b --- /dev/null +++ b/src/hb-ot-gvar-table.hh @@ -0,0 +1,211 @@ +/* + * Copyright © 2018 Adobe 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. + * + * Adobe Author(s): Michiharu Ariza + */ + +#ifndef HB_OT_GVAR_TABLE_HH +#define HB_OT_GVAR_TABLE_HH + +#include "hb-open-type.hh" + +/* + * gvar -- Glyph Variation Table + * https://docs.microsoft.com/en-us/typography/opentype/spec/gvar + */ +#define HB_OT_TAG_gvar HB_TAG('g','v','a','r') + +namespace OT { + +struct Tupple +{ + bool sanitize (hb_sanitize_context_t *c, unsigned int axis_count) const + { + TRACE_SANITIZE (this); + return_trace (coords.sanitize (c, axis_count, this));s + } + + protected: + UnsizedArrayOf + coords; + + public: + DEFINE_SIZE_ARRAY (0, coords); +}; + +struct TupleVarHeader +{ + HBUINT16 varDataSize; + HBUINT16 tupleIndex; + /* Tuple peakTuple */ + /* Tuple intermediateStartTuple */ + /* Tuple intermediateEndTuple */ +}; + +struct GlyphVarData +{ + HBUINT16 tupleVarCount; + OffsetTo + data; + UnsizedArrayOf + tupleVarHeaders; +}; + +struct gvar +{ + static constexpr hb_tag_t tableTag = HB_OT_TAG_gvar; + + bool has_data () const { return version.to_int (); } + + int get_y_origin (hb_codepoint_t glyph) const + { + unsigned int i; + if (!vertYOrigins.bfind (glyph, &i)) + return defaultVertOriginY; + return vertYOrigins[i].vertOriginY; + } + + bool _subset (const hb_subset_plan_t *plan HB_UNUSED, + const gvar *vorg_table, + const hb_vector_t &subset_metrics, + unsigned int dest_sz, + void *dest) const + { + hb_serialize_context_t c (dest, dest_sz); + + gvar *subset_table = c.start_serialize (); + if (unlikely (!c.extend_min (*subset_table))) + return false; + + subset_table->version.major.set (1); + subset_table->version.minor.set (0); + + subset_table->defaultVertOriginY.set (vorg_table->defaultVertOriginY); + subset_table->vertYOrigins.len.set (subset_metrics.length); + + bool success = true; + if (subset_metrics.length > 0) + { + unsigned int size = VertOriginMetric::static_size * subset_metrics.length; + VertOriginMetric *metrics = c.allocate_size (size); + if (likely (metrics != nullptr)) + memcpy (metrics, &subset_metrics[0], size); + else + success = false; + } + c.end_serialize (); + + return success; + } + + bool subset (hb_subset_plan_t *plan) const + { + hb_blob_t *vorg_blob = hb_sanitize_context_t().reference_table (plan->source); + const gvar *vorg_table = vorg_blob->as (); + + /* count the number of glyphs to be included in the subset table */ + hb_vector_t subset_metrics; + subset_metrics.init (); + + + hb_codepoint_t old_glyph = HB_SET_VALUE_INVALID; + unsigned int i = 0; + while (i < vertYOrigins.len + && plan->glyphset ()->next (&old_glyph)) + { + while (old_glyph > vertYOrigins[i].glyph) + { + i++; + if (i >= vertYOrigins.len) + break; + } + + if (old_glyph == vertYOrigins[i].glyph) + { + hb_codepoint_t new_glyph; + if (plan->new_gid_for_old_gid (old_glyph, &new_glyph)) + { + VertOriginMetric *metrics = subset_metrics.push (); + metrics->glyph.set (new_glyph); + metrics->vertOriginY.set (vertYOrigins[i].vertOriginY); + } + } + } + + /* alloc the new table */ + unsigned int dest_sz = gvar::min_size + VertOriginMetric::static_size * subset_metrics.length; + void *dest = (void *) malloc (dest_sz); + if (unlikely (!dest)) + { + subset_metrics.fini (); + hb_blob_destroy (vorg_blob); + return false; + } + + /* serialize the new table */ + if (!_subset (plan, vorg_table, subset_metrics, dest_sz, dest)) + { + subset_metrics.fini (); + free (dest); + hb_blob_destroy (vorg_blob); + return false; + } + + hb_blob_t *result = hb_blob_create ((const char *)dest, + dest_sz, + HB_MEMORY_MODE_READONLY, + dest, + free); + bool success = plan->add_table (HB_OT_TAG_gvar, result); + hb_blob_destroy (result); + subset_metrics.fini (); + hb_blob_destroy (vorg_blob); + return success; + } + + bool sanitize (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && + version.major == 1 && + glyphVarDataArray.sanitize (c)); + } + + protected: + FixedVersion<> version; /* Version of gvar table. Set to 0x00010000u. */ + HBUINT16 axisCount; + HBUINT16 sharedTupleCount; + LOffsetTo sharedTuples; + HBUINT16 glyphCount; + HBUINT16 flags; + LOffsetTo + glyphVarDataArray; + /* OffsetTo glyphVariationDataOffsets */ + + public: + DEFINE_SIZE_ARRAY(8, vertYOrigins); +}; + +} /* namespace OT */ + +#endif /* HB_OT_GVAR_TABLE_HH */ diff --git a/src/hb-ot-layout-common.hh b/src/hb-ot-layout-common.hh index a6e8e3235..4771bf7b8 100644 --- a/src/hb-ot-layout-common.hh +++ b/src/hb-ot-layout-common.hh @@ -33,6 +33,7 @@ #include "hb-ot-layout.hh" #include "hb-open-type.hh" #include "hb-set.hh" +#include "hb-bimap.hh" #ifndef HB_MAX_NESTING_LEVEL @@ -1586,83 +1587,6 @@ static inline void ClassDef_serialize (hb_serialize_context_t *c, hb_array_t klasses) { c->start_embed ()->serialize (c, glyphs, klasses); } -/* Bi-directional map. - * nww ids are assigned incrementally & contiguous; old ids may be random & sparse - * all mappings unique & no duplicate */ -struct hb_map2_t -{ - hb_map2_t () { init (); } - ~hb_map2_t () { fini (); } - - void init (void) - { - count = 0; - old_to_new_map.init (); - new_to_old_map.init (); - } - - void fini (void) - { - old_to_new_map.fini (); - new_to_old_map.fini (); - } - - bool has (hb_codepoint_t _old) const { return old_to_new_map.has (_old); } - - hb_codepoint_t add (hb_codepoint_t _old) - { - hb_codepoint_t _new = old_to_new_map[_old]; - if (_new == HB_MAP_VALUE_INVALID) - { - _new = count++; - old_to_new_map.set (_old, _new); - new_to_old_map.set (_new, _old); - } - return _new; - } - - /* returns HB_MAP_VALUE_INVALID if unmapped */ - hb_codepoint_t operator [] (hb_codepoint_t _old) const { return old_to_new (_old); } - hb_codepoint_t old_to_new (hb_codepoint_t _old) const { return old_to_new_map[_old]; } - hb_codepoint_t new_to_old (hb_codepoint_t _new) const { return new_to_old_map[_new]; } - - bool identity (unsigned int size) - { - hb_codepoint_t i; - old_to_new_map.clear (); - new_to_old_map.clear (); - for (i = 0; i < size; i++) - { - old_to_new_map.set (i, i); - new_to_old_map.set (i, i); - } - count = i; - return old_to_new_map.successful && new_to_old_map.successful; - } - - /* Optional: after finished adding all mappings in a random order, - * reassign new ids to old ids so that they are in the same order. */ - void reorder (void) - { - hb_codepoint_t _new = 0; - for (hb_codepoint_t _old = 0; _new < count; _old++) - { - if (!has (_old)) continue; - new_to_old_map.set (_new, _old); - old_to_new_map.set (_old, _new); - _old++; - _new++; - } - } - - unsigned int get_count () const { return count; } - - protected: - unsigned int count; - hb_map_t old_to_new_map; - hb_map_t new_to_old_map; -}; - /* * Item Variation Store */ @@ -1824,7 +1748,7 @@ struct VarData bool serialize (hb_serialize_context_t *c, const VarData *src, - const hb_map2_t &remap) + const hb_bimap_t &remap) { TRACE_SUBSET (this); if (unlikely (!c->extend_min (*this))) return_trace (false); @@ -1839,7 +1763,7 @@ struct VarData HBUINT8 *p = get_delta_bytes (); for (unsigned int i = 0; i < remap.get_count (); i++) { - memcpy (p, src->get_delta_bytes () + (row_size * remap.new_to_old (i)), row_size); + memcpy (p, src->get_delta_bytes () + (row_size * remap.to_old (i)), row_size); p += row_size; } @@ -1897,7 +1821,7 @@ struct VariationStore bool serialize (hb_serialize_context_t *c, const VariationStore *src, - const hb_array_t &inner_remaps) + const hb_array_t &inner_remaps) { TRACE_SUBSET (this); unsigned int size = min_size + HBUINT32::static_size * inner_remaps.length; diff --git a/src/hb-ot-var-hvar-table.hh b/src/hb-ot-var-hvar-table.hh index 382da7aff..0e711d726 100644 --- a/src/hb-ot-var-hvar-table.hh +++ b/src/hb-ot-var-hvar-table.hh @@ -122,8 +122,8 @@ struct DeltaSetIndexMap struct index_map_subset_plan_t { void init (const DeltaSetIndexMap &index_map, - hb_map2_t &outer_remap, - hb_vector_t &inner_remaps, + hb_bimap_t &outer_remap, + hb_vector_t &inner_remaps, const hb_subset_plan_t *plan) { map_count = 0; @@ -186,8 +186,8 @@ struct index_map_subset_plan_t void remap (const hb_subset_plan_t *plan, const DeltaSetIndexMap *input_map, - const hb_map2_t &outer_remap, - const hb_vector_t &inner_remaps) + const hb_bimap_t &outer_remap, + const hb_vector_t &inner_remaps) { for (unsigned int i = 0; i < max_inners.length; i++) { @@ -260,8 +260,8 @@ struct hvarvvar_subset_plan_t index_map_plans.fini_deep (); } - hb_map2_t outer_remap; - hb_vector_t inner_remaps; + hb_bimap_t outer_remap; + hb_vector_t inner_remaps; hb_vector_t index_map_plans; const VariationStore *var_store; diff --git a/src/hb-subset-cff-common.cc b/src/hb-subset-cff-common.cc index a7e05fe03..4c163ae63 100644 --- a/src/hb-subset-cff-common.cc +++ b/src/hb-subset-cff-common.cc @@ -50,7 +50,7 @@ hb_plan_subset_cff_fdselect (const hb_subset_plan_t *plan, unsigned int &subset_fdselect_size /* OUT */, unsigned int &subset_fdselect_format /* OUT */, hb_vector_t &fdselect_ranges /* OUT */, - hb_map2_t &fdmap /* OUT */) + hb_bimap_t &fdmap /* OUT */) { subset_fd_count = 0; subset_fdselect_size = 0; diff --git a/src/hb-subset-cff-common.hh b/src/hb-subset-cff-common.hh index 2694e0c56..331ddbc22 100644 --- a/src/hb-subset-cff-common.hh +++ b/src/hb-subset-cff-common.hh @@ -541,7 +541,7 @@ struct subr_subset_param_t bool drop_hints; }; -struct subr_remap_t : hb_map2_t +struct subr_remap_t : hb_bimap_t { void create (hb_set_t *closure) { @@ -1012,7 +1012,7 @@ hb_plan_subset_cff_fdselect (const hb_subset_plan_t *plan, unsigned int &subset_fdselect_size /* OUT */, unsigned int &subset_fdselect_format /* OUT */, hb_vector_t &fdselect_ranges /* OUT */, - CFF::hb_map2_t &fdmap /* OUT */); + hb_bimap_t &fdmap /* OUT */); HB_INTERNAL bool hb_serialize_cff_fdselect (hb_serialize_context_t *c, diff --git a/src/hb-subset-cff1.cc b/src/hb-subset-cff1.cc index 05ac471ee..88374fb5e 100644 --- a/src/hb-subset-cff1.cc +++ b/src/hb-subset-cff1.cc @@ -34,12 +34,12 @@ using namespace CFF; -struct remap_sid_t : hb_map2_t +struct remap_sid_t : hb_bimap_t { unsigned int add (unsigned int sid) { if ((sid != CFF_UNDEF_SID) && !is_std_std (sid)) - return offset_sid (hb_map2_t::add (unoffset_sid (sid))); + return offset_sid (hb_bimap_t::add (unoffset_sid (sid))); else return sid; } @@ -49,7 +49,7 @@ struct remap_sid_t : hb_map2_t if (is_std_std (sid) || (sid == CFF_UNDEF_SID)) return sid; else - return offset_sid (hb_map2_t::operator [] (unoffset_sid (sid))); + return offset_sid (hb_bimap_t::operator [] (unoffset_sid (sid))); } static const unsigned int num_std_strings = 391; @@ -850,7 +850,7 @@ struct cff_subset_plan { /* font dict index remap table from fullset FDArray to subset FDArray. * set to CFF_UNDEF_CODE if excluded from subset */ - hb_map2_t fdmap; + hb_bimap_t fdmap; str_buff_vec_t subset_charstrings; str_buff_vec_t subset_globalsubrs; diff --git a/src/hb-subset-cff2.cc b/src/hb-subset-cff2.cc index 1211a166a..a73b1d59e 100644 --- a/src/hb-subset-cff2.cc +++ b/src/hb-subset-cff2.cc @@ -425,7 +425,7 @@ struct cff2_subset_plan { unsigned int subset_fdselect_format; hb_vector_t subset_fdselect_ranges; - hb_map2_t fdmap; + hb_bimap_t fdmap; str_buff_vec_t subset_charstrings; str_buff_vec_t subset_globalsubrs; From 6f91e0d903d7510a4612a9cc7306ec04260cefed Mon Sep 17 00:00:00 2001 From: Michiharu Ariza Date: Tue, 26 Feb 2019 11:11:50 -0800 Subject: [PATCH 08/16] Added hb-ot-var-gvar-table.hh implemented sanitize() placeholder subset() some code cleanup --- src/Makefile.sources | 1 + src/hb-bimap.hh | 6 +- src/hb-ot-gvar-table.hh | 211 ------------------------------------ src/hb-ot-var-gvar-table.hh | 207 +++++++++++++++++++++++++++++++++++ src/hb-ot-var-hvar-table.hh | 14 +-- src/hb-subset.cc | 4 + 6 files changed, 222 insertions(+), 221 deletions(-) delete mode 100644 src/hb-ot-gvar-table.hh create mode 100644 src/hb-ot-var-gvar-table.hh diff --git a/src/Makefile.sources b/src/Makefile.sources index db818283f..a8f609de4 100644 --- a/src/Makefile.sources +++ b/src/Makefile.sources @@ -126,6 +126,7 @@ HB_BASE_sources = \ hb-ot-var-fvar-table.hh \ hb-ot-var-hvar-table.hh \ hb-ot-var-mvar-table.hh \ + hb-ot-var-gvar-table.hh \ hb-ot-var.cc \ hb-ot-vorg-table.hh \ hb-set-digest.hh \ diff --git a/src/hb-bimap.hh b/src/hb-bimap.hh index 1c8fa553c..6ed3e1fbd 100644 --- a/src/hb-bimap.hh +++ b/src/hb-bimap.hh @@ -38,14 +38,14 @@ struct hb_bimap_t hb_bimap_t () { init (); } ~hb_bimap_t () { fini (); } - void init (void) + void init () { count = 0; old_to_new.init (); new_to_old.init (); } - void fini (void) + void fini () { old_to_new.fini (); new_to_old.fini (); @@ -90,7 +90,7 @@ struct hb_bimap_t /* Optional: after finished adding all mappings in a random order, * reassign new ids to old ids so that both are in the same order. */ - void reorder (void) + void reorder () { new_to_old.qsort (cmp_id); for (hb_codepoint_t _new = 0; _new < count; _new++) diff --git a/src/hb-ot-gvar-table.hh b/src/hb-ot-gvar-table.hh deleted file mode 100644 index f0077150b..000000000 --- a/src/hb-ot-gvar-table.hh +++ /dev/null @@ -1,211 +0,0 @@ -/* - * Copyright © 2018 Adobe 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. - * - * Adobe Author(s): Michiharu Ariza - */ - -#ifndef HB_OT_GVAR_TABLE_HH -#define HB_OT_GVAR_TABLE_HH - -#include "hb-open-type.hh" - -/* - * gvar -- Glyph Variation Table - * https://docs.microsoft.com/en-us/typography/opentype/spec/gvar - */ -#define HB_OT_TAG_gvar HB_TAG('g','v','a','r') - -namespace OT { - -struct Tupple -{ - bool sanitize (hb_sanitize_context_t *c, unsigned int axis_count) const - { - TRACE_SANITIZE (this); - return_trace (coords.sanitize (c, axis_count, this));s - } - - protected: - UnsizedArrayOf - coords; - - public: - DEFINE_SIZE_ARRAY (0, coords); -}; - -struct TupleVarHeader -{ - HBUINT16 varDataSize; - HBUINT16 tupleIndex; - /* Tuple peakTuple */ - /* Tuple intermediateStartTuple */ - /* Tuple intermediateEndTuple */ -}; - -struct GlyphVarData -{ - HBUINT16 tupleVarCount; - OffsetTo - data; - UnsizedArrayOf - tupleVarHeaders; -}; - -struct gvar -{ - static constexpr hb_tag_t tableTag = HB_OT_TAG_gvar; - - bool has_data () const { return version.to_int (); } - - int get_y_origin (hb_codepoint_t glyph) const - { - unsigned int i; - if (!vertYOrigins.bfind (glyph, &i)) - return defaultVertOriginY; - return vertYOrigins[i].vertOriginY; - } - - bool _subset (const hb_subset_plan_t *plan HB_UNUSED, - const gvar *vorg_table, - const hb_vector_t &subset_metrics, - unsigned int dest_sz, - void *dest) const - { - hb_serialize_context_t c (dest, dest_sz); - - gvar *subset_table = c.start_serialize (); - if (unlikely (!c.extend_min (*subset_table))) - return false; - - subset_table->version.major.set (1); - subset_table->version.minor.set (0); - - subset_table->defaultVertOriginY.set (vorg_table->defaultVertOriginY); - subset_table->vertYOrigins.len.set (subset_metrics.length); - - bool success = true; - if (subset_metrics.length > 0) - { - unsigned int size = VertOriginMetric::static_size * subset_metrics.length; - VertOriginMetric *metrics = c.allocate_size (size); - if (likely (metrics != nullptr)) - memcpy (metrics, &subset_metrics[0], size); - else - success = false; - } - c.end_serialize (); - - return success; - } - - bool subset (hb_subset_plan_t *plan) const - { - hb_blob_t *vorg_blob = hb_sanitize_context_t().reference_table (plan->source); - const gvar *vorg_table = vorg_blob->as (); - - /* count the number of glyphs to be included in the subset table */ - hb_vector_t subset_metrics; - subset_metrics.init (); - - - hb_codepoint_t old_glyph = HB_SET_VALUE_INVALID; - unsigned int i = 0; - while (i < vertYOrigins.len - && plan->glyphset ()->next (&old_glyph)) - { - while (old_glyph > vertYOrigins[i].glyph) - { - i++; - if (i >= vertYOrigins.len) - break; - } - - if (old_glyph == vertYOrigins[i].glyph) - { - hb_codepoint_t new_glyph; - if (plan->new_gid_for_old_gid (old_glyph, &new_glyph)) - { - VertOriginMetric *metrics = subset_metrics.push (); - metrics->glyph.set (new_glyph); - metrics->vertOriginY.set (vertYOrigins[i].vertOriginY); - } - } - } - - /* alloc the new table */ - unsigned int dest_sz = gvar::min_size + VertOriginMetric::static_size * subset_metrics.length; - void *dest = (void *) malloc (dest_sz); - if (unlikely (!dest)) - { - subset_metrics.fini (); - hb_blob_destroy (vorg_blob); - return false; - } - - /* serialize the new table */ - if (!_subset (plan, vorg_table, subset_metrics, dest_sz, dest)) - { - subset_metrics.fini (); - free (dest); - hb_blob_destroy (vorg_blob); - return false; - } - - hb_blob_t *result = hb_blob_create ((const char *)dest, - dest_sz, - HB_MEMORY_MODE_READONLY, - dest, - free); - bool success = plan->add_table (HB_OT_TAG_gvar, result); - hb_blob_destroy (result); - subset_metrics.fini (); - hb_blob_destroy (vorg_blob); - return success; - } - - bool sanitize (hb_sanitize_context_t *c) const - { - TRACE_SANITIZE (this); - return_trace (c->check_struct (this) && - version.major == 1 && - glyphVarDataArray.sanitize (c)); - } - - protected: - FixedVersion<> version; /* Version of gvar table. Set to 0x00010000u. */ - HBUINT16 axisCount; - HBUINT16 sharedTupleCount; - LOffsetTo sharedTuples; - HBUINT16 glyphCount; - HBUINT16 flags; - LOffsetTo - glyphVarDataArray; - /* OffsetTo glyphVariationDataOffsets */ - - public: - DEFINE_SIZE_ARRAY(8, vertYOrigins); -}; - -} /* namespace OT */ - -#endif /* HB_OT_GVAR_TABLE_HH */ diff --git a/src/hb-ot-var-gvar-table.hh b/src/hb-ot-var-gvar-table.hh new file mode 100644 index 000000000..00d9bdbf3 --- /dev/null +++ b/src/hb-ot-var-gvar-table.hh @@ -0,0 +1,207 @@ +/* + * Copyright © 2019 Adobe 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. + * + * Adobe Author(s): Michiharu Ariza + */ + +#ifndef HB_OT_VAR_GVAR_TABLE_HH +#define HB_OT_VAR_GVAR_TABLE_HH + +#include "hb-open-type.hh" + +/* + * gvar -- Glyph Variation Table + * https://docs.microsoft.com/en-us/typography/opentype/spec/gvar + */ +#define HB_OT_TAG_gvar HB_TAG('g','v','a','r') + +namespace OT { + +struct Tuple : UnsizedArrayOf {}; + +struct TuppleIndex : HBUINT16 +{ + bool has_peak () const { return ((*this) & EmbeddedPeakTuple) != 0; } + bool has_intermediate () const { return ((*this) & IntermediateRegion) != 0; } + bool has_private_points () const { return ((*this) & PrivatePointNumbers) != 0; } + unsigned int get_index () const { return (*this) & TupleIndexMask; } + + protected: + enum Flags { + EmbeddedPeakTuple = 0x8000u, + IntermediateRegion = 0x4000u, + PrivatePointNumbers = 0x2000u, + TupleIndexMask = 0x0FFFu + }; + + public: + DEFINE_SIZE_STATIC (2); +}; + +struct TupleVarHeader +{ + unsigned int get_size (unsigned int axis_count) const + { + return min_size + + (tupleIndex.has_peak ()? get_peak_tuple ().get_size (axis_count): 0) + + (tupleIndex.has_intermediate ()? (get_start_tuple (axis_count).get_size (axis_count) + + get_end_tuple (axis_count).get_size (axis_count)): 0); + } + + const Tuple &get_peak_tuple () const + { return StructAfter (tupleIndex); } + const Tuple &get_start_tuple (unsigned int axis_count) const + { return StructAfter (get_peak_tuple ()[tupleIndex.has_peak ()? axis_count: 0]); } + const Tuple &get_end_tuple (unsigned int axis_count) const + { return StructAfter (get_peak_tuple ()[tupleIndex.has_peak ()? (axis_count * 2): 0]); } + + HBUINT16 varDataSize; + TuppleIndex tupleIndex; + /* UnsizedArrayOf peakTuple - optional */ + /* UnsizedArrayOf intermediateStartTuple - optional */ + /* UnsizedArrayOf intermediateEndTuple - optional */ + + DEFINE_SIZE_MIN (4); +}; + +struct TupleVarCount : HBUINT16 +{ + bool has_shared_point_numbers () const { return ((*this) & SharedPointNumbers) != 0; } + unsigned int get_count () const { return (*this) & CountMask; } + + protected: + enum Flags { + SharedPointNumbers = 0x8000u, + CountMask = 0x0FFFu + }; + + public: + DEFINE_SIZE_STATIC (2); +}; + +struct GlyphVarData +{ + bool check_size (unsigned int axis_count, unsigned int len) const + { return (get_header_size (axis_count) <= len) && (data <= len); } + + unsigned int get_header_size (unsigned int axis_count) const + { + unsigned int size = min_size; + for (unsigned int i = 0; i < tupleVarCount.get_count (); i++) + size += get_tuple_var_header (axis_count, i).get_size (axis_count); // FIX THIS O(n^2) + + return size; + } + + const TupleVarHeader &get_tuple_var_header (unsigned int axis_count, unsigned int i) const + { + const TupleVarHeader *header = &StructAfter(data); + while (i-- > 0) + header = &StructAtOffset (header, header->get_size (axis_count)); + return *header; + } + + TupleVarCount tupleVarCount; + OffsetTo data; + /* TupleVarHeader tupleVarHeaders[] */ + + DEFINE_SIZE_MIN (4); +}; + +struct gvar +{ + static constexpr hb_tag_t tableTag = HB_OT_TAG_gvar; + + bool sanitize_shallow (hb_sanitize_context_t *c) const + { + TRACE_SANITIZE (this); + return_trace (c->check_struct (this) && (version.major == 1) && + (glyphCount == c->get_num_glyphs ()) && + c->check_array (&(this+sharedTuples), axisCount * sharedTupleCount) && + (is_long_offset ()? + c->check_array (get_long_offset_array (), glyphCount+1): + c->check_array (get_short_offset_array (), glyphCount+1)) && + c->check_array (((const HBUINT8*)&(this+dataZ)) + get_offset (0), + get_offset (glyphCount) - get_offset (0))); + } + + /* GlyphVarData not sanitized here; must be checked while accessing each glyph varation data */ + bool sanitize (hb_sanitize_context_t *c) const + { return sanitize_shallow (c); } + + bool subset (hb_subset_context_t *c) const + { return true; } // TOOD + + protected: + const GlyphVarData *get_glyph_var_data (unsigned int gid, unsigned int *length/*OUT*/) const + { + unsigned int start_offset = get_offset (gid); + unsigned int end_offset = get_offset (gid+1); + + if ((start_offset == end_offset) || + unlikely ((start_offset > get_offset (glyphCount)) || + (start_offset + GlyphVarData::min_size > end_offset))) + { + *length = 0; + return &Null(GlyphVarData); + } + const GlyphVarData *var_data = &(((unsigned char *)this+start_offset)+dataZ); + if (unlikely (!var_data->check_size (axisCount, end_offset - start_offset))) + { + *length = 0; + return &Null (GlyphVarData); + } + *length = end_offset - start_offset; + return var_data; + } + + bool is_long_offset () const { return (flags & 1)!=0; } + + unsigned int get_offset (unsigned int gid) const + { + if (is_long_offset ()) + return get_long_offset_array ()[gid]; + else + return get_short_offset_array ()[gid] * 2; + } + + const HBUINT32 *get_long_offset_array () const { return (const HBUINT32 *)&offsetZ; } + const HBUINT16 *get_short_offset_array () const { return (const HBUINT16 *)&offsetZ; } + + protected: + FixedVersion<> version; /* Version of gvar table. Set to 0x00010000u. */ + HBUINT16 axisCount; + HBUINT16 sharedTupleCount; + LOffsetTo sharedTuples; /* Actually LOffsetTo> */ + HBUINT16 glyphCount; + HBUINT16 flags; + LOffsetTo dataZ; /* Array of GlyphVarData */ + UnsizedArrayOf offsetZ; /* Array of 16-bit or 32-bit (glyphCount+1) offsets */ + + public: + DEFINE_SIZE_MIN (20); +}; + +} /* namespace OT */ + +#endif /* HB_OT_VAR_GVAR_TABLE_HH */ diff --git a/src/hb-ot-var-hvar-table.hh b/src/hb-ot-var-hvar-table.hh index 0e711d726..97e70605c 100644 --- a/src/hb-ot-var-hvar-table.hh +++ b/src/hb-ot-var-hvar-table.hh @@ -178,7 +178,7 @@ struct index_map_subset_plan_t } } - void fini (void) + void fini () { max_inners.fini (); output_map.fini (); @@ -207,14 +207,14 @@ struct index_map_subset_plan_t } } - unsigned int get_inner_bit_count (void) const { return inner_bit_count; } - unsigned int get_width (void) const { return ((outer_bit_count + inner_bit_count + 7) / 8); } - unsigned int get_map_count (void) const { return map_count; } + unsigned int get_inner_bit_count () const { return inner_bit_count; } + unsigned int get_width () const { return ((outer_bit_count + inner_bit_count + 7) / 8); } + unsigned int get_map_count () const { return map_count; } - unsigned int get_size (void) const + unsigned int get_size () const { return (map_count? (DeltaSetIndexMap::min_size + get_width () * map_count): 0); } - hb_array_t get_output_map (void) const { return output_map.as_array (); } + hb_array_t get_output_map () const { return output_map.as_array (); } protected: unsigned int map_count; @@ -254,7 +254,7 @@ struct hvarvvar_subset_plan_t index_map_plans[i].remap (plan, index_maps[i], outer_remap, inner_remaps); } - void fini (void) + void fini () { inner_remaps.fini_deep (); index_map_plans.fini_deep (); diff --git a/src/hb-subset.cc b/src/hb-subset.cc index 4441575ab..cff7c566f 100644 --- a/src/hb-subset.cc +++ b/src/hb-subset.cc @@ -45,6 +45,7 @@ #include "hb-ot-vorg-table.hh" #include "hb-ot-layout-gsub-table.hh" #include "hb-ot-layout-gpos-table.hh" +#include "hb-ot-var-gvar-table.hh" #include "hb-ot-var-hvar-table.hh" @@ -199,6 +200,9 @@ _subset_table (hb_subset_plan_t *plan, case HB_OT_TAG_GPOS: result = _subset2 (plan); break; + case HB_OT_TAG_gvar: + result = _subset2 (plan); + break; case HB_OT_TAG_HVAR: result = _subset2 (plan); break; From 33354ab6b87211ae2e702bee162fa4260078e70a Mon Sep 17 00:00:00 2001 From: Michiharu Ariza Date: Tue, 26 Feb 2019 16:54:00 -0800 Subject: [PATCH 09/16] gvar::subset() --- src/hb-ot-var-gvar-table.hh | 81 ++++++++++++++++++++++++++++++++----- 1 file changed, 71 insertions(+), 10 deletions(-) diff --git a/src/hb-ot-var-gvar-table.hh b/src/hb-ot-var-gvar-table.hh index 00d9bdbf3..de672ed7f 100644 --- a/src/hb-ot-var-gvar-table.hh +++ b/src/hb-ot-var-gvar-table.hh @@ -150,10 +150,75 @@ struct gvar { return sanitize_shallow (c); } bool subset (hb_subset_context_t *c) const - { return true; } // TOOD + { + TRACE_SUBSET (this); + + gvar *out = c->serializer->allocate_min (); + if (unlikely (!out)) return_trace (false); + + out->version.major.set (1); + out->version.minor.set (0); + out->axisCount.set (axisCount); + out->sharedTupleCount.set (sharedTupleCount); + + unsigned int num_glyphs = c->plan->num_output_glyphs (); + out->glyphCount.set (num_glyphs); + + unsigned int subset_data_size = 0; + for (hb_codepoint_t gid = 0; gid < num_glyphs; gid++) + { + unsigned int old_gid; + if (!c->plan->old_gid_for_new_gid (gid, &old_gid)) continue; + subset_data_size += get_data_length (old_gid); + } + + bool long_offset = subset_data_size & ~0xFFFFu; + out->flags.set (long_offset? 1: 0); + + HBUINT8 *subset_offsets = c->serializer->allocate_size ((long_offset?HBUINT32::static_size: HBUINT16::static_size) * (num_glyphs+1)); + if (!subset_offsets) return_trace (false); + + char *subset_data = c->serializer->allocate_size(subset_data_size); + if (!subset_data) return_trace (false); + out->dataZ.set (subset_data - (char *)out); + + unsigned int glyph_offset = 0; + for (hb_codepoint_t gid = 0; gid < num_glyphs; gid++) + { + unsigned int old_gid; + unsigned int length = c->plan->old_gid_for_new_gid (gid, &old_gid)? get_data_length (old_gid): 0; + + if (long_offset) + ((HBUINT32 *)subset_offsets)[gid].set (glyph_offset); + else + ((HBUINT16 *)subset_offsets)[gid].set (glyph_offset / 2); + + if (length > 0) memcpy (subset_data, get_glyph_var_data (old_gid), length); + subset_data += length; + glyph_offset += length; + } + if (long_offset) + ((HBUINT32 *)subset_offsets)[num_glyphs].set (glyph_offset); + else + ((HBUINT16 *)subset_offsets)[num_glyphs].set (glyph_offset / 2); + + /* shared tuples */ + if (!sharedTupleCount || !sharedTuples) + out->sharedTuples.set (0); + else + { + unsigned int shared_tuple_size = F2DOT14::static_size * axisCount * sharedTupleCount; + F2DOT14 *tuples = c->serializer->allocate_size (shared_tuple_size); + if (!tuples) return_trace (false); + out->sharedTuples.set ((char *)tuples - (char *)out); + memcpy (tuples, &(this+sharedTuples), shared_tuple_size); + } + + return_trace (true); + } protected: - const GlyphVarData *get_glyph_var_data (unsigned int gid, unsigned int *length/*OUT*/) const + const GlyphVarData *get_glyph_var_data (unsigned int gid) const { unsigned int start_offset = get_offset (gid); unsigned int end_offset = get_offset (gid+1); @@ -161,17 +226,10 @@ struct gvar if ((start_offset == end_offset) || unlikely ((start_offset > get_offset (glyphCount)) || (start_offset + GlyphVarData::min_size > end_offset))) - { - *length = 0; return &Null(GlyphVarData); - } const GlyphVarData *var_data = &(((unsigned char *)this+start_offset)+dataZ); if (unlikely (!var_data->check_size (axisCount, end_offset - start_offset))) - { - *length = 0; return &Null (GlyphVarData); - } - *length = end_offset - start_offset; return var_data; } @@ -185,6 +243,9 @@ struct gvar return get_short_offset_array ()[gid] * 2; } + unsigned int get_data_length (unsigned int gid) const + { return get_offset (gid+1) - get_offset (gid); } + const HBUINT32 *get_long_offset_array () const { return (const HBUINT32 *)&offsetZ; } const HBUINT16 *get_short_offset_array () const { return (const HBUINT16 *)&offsetZ; } @@ -192,7 +253,7 @@ struct gvar FixedVersion<> version; /* Version of gvar table. Set to 0x00010000u. */ HBUINT16 axisCount; HBUINT16 sharedTupleCount; - LOffsetTo sharedTuples; /* Actually LOffsetTo> */ + LOffsetTo sharedTuples; /* LOffsetTo> */ HBUINT16 glyphCount; HBUINT16 flags; LOffsetTo dataZ; /* Array of GlyphVarData */ From fa2b3d30f6562a7457acca205f1bf544089c88ba Mon Sep 17 00:00:00 2001 From: Michiharu Ariza Date: Fri, 1 Mar 2019 15:14:22 -0800 Subject: [PATCH 10/16] calculate VF advance widths from gvar & glyf --- src/hb-ot-glyf-table.hh | 151 +++++++++++++ src/hb-ot-hmtx-table.hh | 23 +- src/hb-ot-var-gvar-table.hh | 412 ++++++++++++++++++++++++++++++++---- 3 files changed, 543 insertions(+), 43 deletions(-) diff --git a/src/hb-ot-glyf-table.hh b/src/hb-ot-glyf-table.hh index c2b38b05b..028722c49 100644 --- a/src/hb-ot-glyf-table.hh +++ b/src/hb-ot-glyf-table.hh @@ -22,6 +22,7 @@ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. * * Google Author(s): Behdad Esfahbod + * Adobe Author(s): Michiharu Ariza */ #ifndef HB_OT_GLYF_TABLE_HH @@ -281,6 +282,156 @@ struct glyf FLAG_RESERVED2 = 0x80 }; + enum phantom_point_index_t { + PHANTOM_LEFT = 0, + PHANTOM_RIGHT = 1, + PHANTOM_TOP = 2, + PHANTOM_BOTTOM = 3, + PHANTOM_COUNT = 4 + }; + + struct contour_point_t + { + void init () { flag = 0; x = y = 0.0f; } + uint8_t flag; + float x, y; + }; + + struct range_checker_t + { + range_checker_t (const void *_table, unsigned int _start_offset, unsigned int _end_offset) + : table ((const char*)_table), start_offset (_start_offset), end_offset (_end_offset) {} + + template + bool in_range (const T *p) const + { + return ((const char *) p) >= table + start_offset + && ((const char *) (p + T::static_size)) <= table + end_offset; + } + + protected: + const char *table; + const unsigned int start_offset; + const unsigned int end_offset; + }; + + struct x_setter_t + { + void set (contour_point_t &point, float v) const { point.x = v; } + bool is_short (uint8_t flag) const { return (flag & FLAG_X_SHORT) != 0; } + bool is_same (uint8_t flag) const { return (flag & FLAG_X_SAME) != 0; } + }; + + struct y_setter_t + { + void set (contour_point_t &point, float v) const { point.y = v; } + bool is_short (uint8_t flag) const { return (flag & FLAG_Y_SHORT) != 0; } + bool is_same (uint8_t flag) const { return (flag & FLAG_Y_SAME) != 0; } + }; + + template + static bool read_points (const HBUINT8 *&p /* IN/OUT */, + hb_vector_t &_points /* IN/OUT */, + const range_checker_t &checker) + { + const T coord_setter; + float v = 0; + for (unsigned int i = 0; i < _points.length - PHANTOM_COUNT; i++) + { + uint8_t flag = _points[i].flag; + if (coord_setter.is_short (flag)) + { + if (unlikely (!checker.in_range (p))) return false; + if (coord_setter.is_same (flag)) + v += *p++; + else + v -= *p++; + } + else + { + if (unlikely (!checker.in_range ((const HBUINT16 *)p))) return false; + if (!coord_setter.is_same (flag)) + { + v = *(const HBINT16 *)p; + p += HBINT16::static_size; + } + } + coord_setter.set (_points[i], v); + } + return true; + }; + + /* for a simple glyph, return contour end points, flags, along with coordinate points + * for a composite glyph, return pseudo component points + * in both cases points trailed with four phantom points + */ + bool get_contour_points (hb_codepoint_t glyph, + bool phantom_only, + hb_vector_t &_points /* OUT */, + hb_vector_t &_end_points /* OUT */) const + { + unsigned int num_points = 0; + unsigned int start_offset, end_offset; + if (unlikely (!get_offsets (glyph, &start_offset, &end_offset))) + return false; + if (end_offset - start_offset < GlyphHeader::static_size) + return false; + + const GlyphHeader &glyph_header = StructAtOffset (glyf_table, start_offset); + if (unlikely (glyph_header.numberOfContours < 0)) return false; + int16_t num_contours = (int16_t) glyph_header.numberOfContours; + const HBUINT16 *end_pts = &StructAfter (glyph_header); + + range_checker_t checker (glyf_table, start_offset, end_offset); + num_points = 0; + if (num_contours > 0) + { + if (unlikely (!checker.in_range (&end_pts[num_contours + 1]))) return false; + num_points = end_pts[num_contours - 1] + 1; + } + else if (num_contours < 0) + { + CompositeGlyphHeader::Iterator composite; + if (!get_composite (glyph, &composite)) return false; + do + { + num_points++; + } while (composite.move_to_next()); + } + + _points.resize (num_points + PHANTOM_COUNT); + for (unsigned int i = 0; i < _points.length; i++) _points[i].init (); + if ((num_contours <= 0) || phantom_only) return true; + + /* Read simple glyph points if !phantom_only */ + _end_points.resize (num_contours); + + for (int16_t i = 0; i < num_contours; i++) + _end_points[i] = end_pts[i]; + + /* Skip instructions */ + const HBUINT8 *p = &StructAtOffset (&end_pts[num_contours+1], end_pts[num_contours]); + + /* Read flags */ + for (unsigned int i = 0; i < num_points; i++) + { + if (unlikely (!checker.in_range (p))) return false; + uint8_t flag = *p++; + _points[i].flag = flag; + if ((flag & FLAG_REPEAT) != 0) + { + if (unlikely (!checker.in_range (p))) return false; + unsigned int repeat_count = *p++; + while ((repeat_count-- > 0) && (++i < num_points)) + _points[i].flag = flag; + } + } + + /* Read x & y coordinates */ + return (!read_points (p, _points, checker) && + !read_points (p, _points, checker)); + } + /* based on FontTools _g_l_y_f.py::trim */ bool remove_padding (unsigned int start_offset, unsigned int *end_offset) const diff --git a/src/hb-ot-hmtx-table.hh b/src/hb-ot-hmtx-table.hh index 9ef1f5716..4c1693f48 100644 --- a/src/hb-ot-hmtx-table.hh +++ b/src/hb-ot-hmtx-table.hh @@ -31,6 +31,7 @@ #include "hb-ot-hhea-table.hh" #include "hb-ot-os2-table.hh" #include "hb-ot-var-hvar-table.hh" +#include "hb-ot-var-gvar-table.hh" /* * hmtx -- Horizontal Metrics @@ -163,6 +164,7 @@ struct hmtxvmtx void init (hb_face_t *face, unsigned int default_advance_ = 0) { + memset (this, 0, sizeof (*this)); default_advance = default_advance_ ? default_advance_ : hb_face_get_upem (face); bool got_font_extents = false; @@ -206,12 +208,24 @@ struct hmtxvmtx } var_table = hb_sanitize_context_t().reference_table (face, T::variationsTag); + + /* If a TrueType variable font has no HVAR/VVAR table, gvar & glyf table is required for metrics calculation */ + if (var_table.get_blob () == hb_blob_get_empty ()) + { + hb_blob_ptr_t fvar_table = hb_sanitize_context_t().reference_table (face, HB_OT_TAG_fvar); + if (fvar_table.get_blob () != hb_blob_get_empty ()) + { + gvar_accel.init (face); + } + fvar_table.destroy (); + } } void fini () { table.destroy (); var_table.destroy (); + gvar_accel.fini (); } /* TODO Add variations version. */ @@ -249,7 +263,13 @@ struct hmtxvmtx unsigned int advance = get_advance (glyph); if (likely (glyph < num_metrics)) { - advance += (font->num_coords ? var_table->get_advance_var (glyph, font->coords, font->num_coords) : 0); // TODO Optimize?! + if (font->num_coords) + { + if (var_table.get_blob () != hb_blob_get_empty ()) + advance += var_table->get_advance_var (glyph, font->coords, font->num_coords); // TODO Optimize?! + else + advance += gvar_accel.get_advance_var (glyph, font->coords, font->num_coords, T::tableTag==HB_OT_TAG_vmtx); + } } return advance; } @@ -294,6 +314,7 @@ struct hmtxvmtx private: hb_blob_ptr_t table; hb_blob_ptr_t var_table; + gvar::accelerator_t gvar_accel; }; protected: diff --git a/src/hb-ot-var-gvar-table.hh b/src/hb-ot-var-gvar-table.hh index de672ed7f..bba8f9b23 100644 --- a/src/hb-ot-var-gvar-table.hh +++ b/src/hb-ot-var-gvar-table.hh @@ -28,6 +28,8 @@ #define HB_OT_VAR_GVAR_TABLE_HH #include "hb-open-type.hh" +#include "hb-ot-glyf-table.hh" +#include "hb-ot-var-fvar-table.hh" /* * gvar -- Glyph Variation Table @@ -41,12 +43,6 @@ struct Tuple : UnsizedArrayOf {}; struct TuppleIndex : HBUINT16 { - bool has_peak () const { return ((*this) & EmbeddedPeakTuple) != 0; } - bool has_intermediate () const { return ((*this) & IntermediateRegion) != 0; } - bool has_private_points () const { return ((*this) & PrivatePointNumbers) != 0; } - unsigned int get_index () const { return (*this) & TupleIndexMask; } - - protected: enum Flags { EmbeddedPeakTuple = 0x8000u, IntermediateRegion = 0x4000u, @@ -54,7 +50,6 @@ struct TuppleIndex : HBUINT16 TupleIndexMask = 0x0FFFu }; - public: DEFINE_SIZE_STATIC (2); }; @@ -63,17 +58,77 @@ struct TupleVarHeader unsigned int get_size (unsigned int axis_count) const { return min_size + - (tupleIndex.has_peak ()? get_peak_tuple ().get_size (axis_count): 0) + - (tupleIndex.has_intermediate ()? (get_start_tuple (axis_count).get_size (axis_count) + - get_end_tuple (axis_count).get_size (axis_count)): 0); + (has_peak ()? get_peak_tuple ().get_size (axis_count): 0) + + (has_intermediate ()? (get_start_tuple (axis_count).get_size (axis_count) + + get_end_tuple (axis_count).get_size (axis_count)): 0); } + const TupleVarHeader &get_next (unsigned int axis_count) const + { return StructAtOffset (this, get_size (axis_count)); } + + float calculate_scalar (const int *coords, unsigned int coord_count, + const hb_array_t shared_tuples) const + { + const F2DOT14 *peak_tuple; + + if (has_peak ()) + peak_tuple = &(get_peak_tuple ()[0]); + else + { + unsigned int index = get_index (); + if (unlikely (index * coord_count >= shared_tuples.length)) + return 0.f; + peak_tuple = &shared_tuples[coord_count * index]; + } + + const F2DOT14 *start_tuple = nullptr; + const F2DOT14 *end_tuple = nullptr; + if (has_intermediate ()) + { + start_tuple = get_start_tuple (coord_count); + end_tuple = get_end_tuple (coord_count); + } + + float scalar = 1.f; + for (unsigned int i = 0; i < coord_count; i++) + { + int v = coords[i]; + int peak = peak_tuple[i]; + if (!peak || v == peak) continue; + + if (has_intermediate ()) + { + int start = start_tuple[i]; + int end = end_tuple[i]; + if (unlikely (start > peak || peak > end || + start < 0 && end > 0 && peak)) continue; + if (v < start || v > end) return 0.f; + if (v < peak) + { if (peak != start) scalar *= (float)(v - start) / (peak - start); } + else + { if (peak != end) scalar *= (float)(end - v) / (end - peak); } + } + else if (!v || v < MIN (0, peak) || v > MAX (0, peak)) return 0.f; + else + scalar *= (float)v / peak; + } + return scalar; + } + + unsigned int get_data_size () const { return varDataSize; } + + bool has_peak () const { return (tupleIndex & TuppleIndex::EmbeddedPeakTuple) != 0; } + bool has_intermediate () const { return (tupleIndex & TuppleIndex::IntermediateRegion) != 0; } + bool has_private_points () const { return (tupleIndex & TuppleIndex::PrivatePointNumbers) != 0; } + unsigned int get_index () const { return (tupleIndex & TuppleIndex::TupleIndexMask); } + + protected: const Tuple &get_peak_tuple () const { return StructAfter (tupleIndex); } const Tuple &get_start_tuple (unsigned int axis_count) const - { return StructAfter (get_peak_tuple ()[tupleIndex.has_peak ()? axis_count: 0]); } + { return StructAfter (get_peak_tuple ()[has_peak ()? axis_count: 0]); } const Tuple &get_end_tuple (unsigned int axis_count) const - { return StructAfter (get_peak_tuple ()[tupleIndex.has_peak ()? (axis_count * 2): 0]); } + { return StructAfter (get_peak_tuple ()[has_peak ()? (axis_count * 2): 0]); } HBUINT16 varDataSize; TuppleIndex tupleIndex; @@ -81,6 +136,7 @@ struct TupleVarHeader /* UnsizedArrayOf intermediateStartTuple - optional */ /* UnsizedArrayOf intermediateEndTuple - optional */ + public: DEFINE_SIZE_MIN (4); }; @@ -101,30 +157,72 @@ struct TupleVarCount : HBUINT16 struct GlyphVarData { - bool check_size (unsigned int axis_count, unsigned int len) const - { return (get_header_size (axis_count) <= len) && (data <= len); } + const TupleVarHeader &get_tuple_var_header (void) const + { return StructAfter(data); } - unsigned int get_header_size (unsigned int axis_count) const + struct tuple_iterator_t { - unsigned int size = min_size; - for (unsigned int i = 0; i < tupleVarCount.get_count (); i++) - size += get_tuple_var_header (axis_count, i).get_size (axis_count); // FIX THIS O(n^2) - - return size; + void init (const GlyphVarData *_var_data, unsigned int _length, unsigned int _axis_count) + { + var_data = _var_data; + length = _length; + index = 0; + axis_count = _axis_count; + current_tuple = &var_data->get_tuple_var_header (); + data_offset = 0; + } + + bool is_valid () const + { + return (index < var_data->tupleVarCount.get_count ()) && + in_range (current_tuple) && + current_tuple->get_size (axis_count); + }; + + bool move_to_next () + { + data_offset += current_tuple->get_data_size (); + current_tuple = ¤t_tuple->get_next (axis_count); + index++; + return is_valid (); + } + + bool in_range (const void *p, unsigned int l) const + { return (const char*)p >= (const char*)var_data && (const char*)p+l <= (const char*)var_data + length; } + + template bool in_range (const T *p) const { return in_range (p, sizeof (*p)); } + + const HBUINT8 *get_serialized_data () const + { return &(var_data+var_data->data) + data_offset; } + + private: + const GlyphVarData *var_data; + unsigned int length; + unsigned int index; + unsigned int axis_count; + unsigned int data_offset; + + public: + const TupleVarHeader *current_tuple; + }; + + static bool get_tuple_iterator (const GlyphVarData *var_data, + unsigned int length, + unsigned int axis_count, + tuple_iterator_t *iterator /* OUT */) + { + iterator->init (var_data, length, axis_count); + return iterator->is_valid (); } - const TupleVarHeader &get_tuple_var_header (unsigned int axis_count, unsigned int i) const - { - const TupleVarHeader *header = &StructAfter(data); - while (i-- > 0) - header = &StructAtOffset (header, header->get_size (axis_count)); - return *header; - } + bool has_shared_point_numbers () const { return tupleVarCount.has_shared_point_numbers (); } + protected: TupleVarCount tupleVarCount; OffsetTo data; /* TupleVarHeader tupleVarHeaders[] */ + public: DEFINE_SIZE_MIN (4); }; @@ -169,13 +267,13 @@ struct gvar { unsigned int old_gid; if (!c->plan->old_gid_for_new_gid (gid, &old_gid)) continue; - subset_data_size += get_data_length (old_gid); + subset_data_size += get_glyph_var_data_length (old_gid); } bool long_offset = subset_data_size & ~0xFFFFu; out->flags.set (long_offset? 1: 0); - HBUINT8 *subset_offsets = c->serializer->allocate_size ((long_offset?HBUINT32::static_size: HBUINT16::static_size) * (num_glyphs+1)); + HBUINT8 *subset_offsets = c->serializer->allocate_size ((long_offset? 4: 2) * (num_glyphs+1)); if (!subset_offsets) return_trace (false); char *subset_data = c->serializer->allocate_size(subset_data_size); @@ -186,7 +284,7 @@ struct gvar for (hb_codepoint_t gid = 0; gid < num_glyphs; gid++) { unsigned int old_gid; - unsigned int length = c->plan->old_gid_for_new_gid (gid, &old_gid)? get_data_length (old_gid): 0; + unsigned int length = c->plan->old_gid_for_new_gid (gid, &old_gid)? get_glyph_var_data_length (old_gid): 0; if (long_offset) ((HBUINT32 *)subset_offsets)[gid].set (glyph_offset); @@ -218,37 +316,267 @@ struct gvar } protected: - const GlyphVarData *get_glyph_var_data (unsigned int gid) const + const GlyphVarData *get_glyph_var_data (hb_codepoint_t glyph) const { - unsigned int start_offset = get_offset (gid); - unsigned int end_offset = get_offset (gid+1); + unsigned int start_offset = get_offset (glyph); + unsigned int end_offset = get_offset (glyph+1); if ((start_offset == end_offset) || unlikely ((start_offset > get_offset (glyphCount)) || (start_offset + GlyphVarData::min_size > end_offset))) return &Null(GlyphVarData); - const GlyphVarData *var_data = &(((unsigned char *)this+start_offset)+dataZ); - if (unlikely (!var_data->check_size (axisCount, end_offset - start_offset))) - return &Null (GlyphVarData); - return var_data; + return &(((unsigned char *)this+start_offset)+dataZ); } bool is_long_offset () const { return (flags & 1)!=0; } - unsigned int get_offset (unsigned int gid) const + unsigned int get_offset (unsigned int i) const { if (is_long_offset ()) - return get_long_offset_array ()[gid]; + return get_long_offset_array ()[i]; else - return get_short_offset_array ()[gid] * 2; + return get_short_offset_array ()[i] * 2; } - unsigned int get_data_length (unsigned int gid) const - { return get_offset (gid+1) - get_offset (gid); } + unsigned int get_glyph_var_data_length (unsigned int glyph) const + { return get_offset (glyph+1) - get_offset (glyph); } const HBUINT32 *get_long_offset_array () const { return (const HBUINT32 *)&offsetZ; } const HBUINT16 *get_short_offset_array () const { return (const HBUINT16 *)&offsetZ; } + typedef glyf::accelerator_t::contour_point_t contour_point_t; + typedef glyf::accelerator_t::phantom_point_index_t pp_t; + typedef glyf::accelerator_t::range_checker_t range_checker_t; + + public: + struct accelerator_t + { + void init (hb_face_t *face) + { + memset (this, 0, sizeof (accelerator_t)); + + gvar_table = hb_sanitize_context_t ().reference_table (face); + glyf.init (face); + hb_blob_ptr_t fvar_table = hb_sanitize_context_t ().reference_table (face); + unsigned int axis_count = fvar_table->get_axis_count (); + fvar_table.destroy (); + + if (unlikely ((gvar_table->glyphCount != face->get_num_glyphs ()) || + (gvar_table->axisCount != axis_count))) + fini (); + + unsigned int num_shared_coord = gvar_table->sharedTupleCount * gvar_table->axisCount; + shared_tuples.resize (num_shared_coord); + for (unsigned int i = 0; i < num_shared_coord; i++) + shared_tuples[i] = (&(gvar_table+gvar_table->sharedTuples))[i]; + } + + void fini () + { + gvar_table.destroy (); + glyf.fini (); + } + + bool apply_deltas_to_points (hb_codepoint_t glyph, + const int *coords, unsigned int coord_count, + const hb_array_t points, + const hb_array_t end_points) const + { + if (unlikely (coord_count != gvar_table->axisCount)) return false; + + const GlyphVarData *var_data = gvar_table->get_glyph_var_data (glyph); + GlyphVarData::tuple_iterator_t iterator; + if (!GlyphVarData::get_tuple_iterator (var_data, + gvar_table->get_glyph_var_data_length (glyph), + gvar_table->axisCount, + &iterator)) + return false; + + do { + float scalar = iterator.current_tuple->calculate_scalar (coords, coord_count, shared_tuples.as_array ()); + if (scalar == 0.f) continue; + const HBUINT8 *p = iterator.get_serialized_data (); + unsigned int length = iterator.current_tuple->get_data_size (); + if (unlikely (!iterator.in_range (p, length))) return false; + + range_checker_t checker (p, 0, length); + hb_vector_t shared_indices; + if (var_data->has_shared_point_numbers () && + !unpack_points (p, shared_indices, checker)) return false; + hb_vector_t private_indices; + if (iterator.current_tuple->has_private_points () && + !unpack_points (p, private_indices, checker)) return false; + const hb_array_t &indices = shared_indices.length? shared_indices: private_indices; + + bool apply_to_all = (indices.length == 0); + unsigned int num_deltas = apply_to_all? points.length: indices.length; + hb_vector_t x_deltas; + x_deltas.resize (num_deltas); + if (!unpack_deltas (p, x_deltas, checker)) return false; + hb_vector_t y_deltas; + y_deltas.resize (num_deltas); + if (!unpack_deltas (p, y_deltas, checker)) return false; + + for (unsigned int i = 0; i < num_deltas; i++) + { + unsigned int pt_index = apply_to_all? i: indices[i]; + points[pt_index].x += x_deltas[i] * scalar; + points[pt_index].y += y_deltas[i] * scalar; + } + /* TODO: interpolate untouched points for glyph extents */ + } while (iterator.move_to_next ()); + + return true; + } + + /* Note: Recursively calls itself. Who's checking recursively nested composite glyph BTW? */ + bool get_var_metrics (hb_codepoint_t glyph, + const int *coords, unsigned int coord_count, + hb_vector_t &phantoms) const + { + hb_vector_t points; + hb_vector_t end_points; + if (!glyf.get_contour_points (glyph, true, points, end_points)) return false; + if (!apply_deltas_to_points (glyph, coords, coord_count, points.as_array (), end_points.as_array ())) return false; + + for (unsigned int i = 0; i < pp_t::PHANTOM_COUNT; i++) + phantoms[i] = points[points.length - pp_t::PHANTOM_COUNT + i]; + + glyf::CompositeGlyphHeader::Iterator composite; + if (!glyf.get_composite (glyph, &composite)) return true; /* simple glyph */ + do + { + /* TODO: support component scale/transformation */ + if (((composite.current->flags & glyf::CompositeGlyphHeader::USE_MY_METRICS) != 0) && + !get_var_metrics (composite.current->glyphIndex, coords, coord_count, phantoms)) + return false; + } while (composite.move_to_next()); + return true; + } + + float get_advance_var (hb_codepoint_t glyph, + const int *coords, unsigned int coord_count, + bool vertical) const + { + float advance = 0.f; + if (coord_count != gvar_table->axisCount) return advance; + + hb_vector_t points; + points.resize (pp_t::PHANTOM_COUNT); + + if (!get_var_metrics (glyph, coords, coord_count, points)) + return advance; + + if (vertical) + return -(points[pp_t::PHANTOM_BOTTOM].y - points[pp_t::PHANTOM_TOP].y); // is this sign correct? + else + return points[pp_t::PHANTOM_RIGHT].x - points[pp_t::PHANTOM_LEFT].x; + } + + protected: + const GlyphVarData *get_glyph_var_data (hb_codepoint_t glyph) const + { return gvar_table->get_glyph_var_data (glyph); } + + static bool unpack_points (const HBUINT8 *&p /* IN/OUT */, + hb_vector_t &points /* OUT */, + const range_checker_t &check) + { + enum packed_point_flag_t + { + POINTS_ARE_WORDS = 0x80, + POINT_RUN_COUNT_MASK = 0x7F + }; + + if (!check.in_range (p)) return false; + uint16_t count = *p++; + if ((count & POINTS_ARE_WORDS) != 0) + { + if (!check.in_range (p)) return false; + count = ((count & POINT_RUN_COUNT_MASK) << 8) | *p++; + } + points.resize (count); + + uint16_t i = 0; + while (i < count) + { + if (!check.in_range (p)) return false; + uint16_t j; + uint8_t control = *p++; + uint16_t run_count = (control & POINT_RUN_COUNT_MASK) + 1; + if ((control & POINTS_ARE_WORDS) != 0) + { + for (j = 0; j < run_count && i < count; j++, i++) + { + if (!check.in_range ((const HBUINT16 *)p)) return false; + points[i] = *(const HBUINT16 *)p; + p += HBUINT16::static_size; + } + } + else + { + for (j = 0; j < run_count && i < count; j++, i++) + { + if (!check.in_range (p)) return false; + points[i] = *p++; + } + } + if (j < run_count) return false; + } + return true; + } + + static bool unpack_deltas (const HBUINT8 *&p /* IN/OUT */, + hb_vector_t &deltas /* IN/OUT */, + const range_checker_t &check) + { + enum packed_delta_flag_t + { + DELTAS_ARE_ZERO = 0x80, + DELTAS_ARE_WORDS = 0x40, + DELTA_RUN_COUNT_MASK = 0x3F + }; + + unsigned int i = 0; + unsigned int count = deltas.length; + while (i < count) + { + if (!check.in_range (p)) return false; + uint16_t j; + uint8_t control = *p++; + uint16_t run_count = (control & DELTA_RUN_COUNT_MASK) + 1; + if ((control & DELTAS_ARE_ZERO) != 0) + { + for (j = 0; j < run_count && i < count; j++, i++) + deltas[i] = 0; + } + else if ((control & DELTAS_ARE_WORDS) != 0) + { + for (j = 0; j < run_count && i < count; j++, i++) + { + if (!check.in_range ((const HBUINT16 *)p)) return false; + deltas[i] = *(const HBINT16 *)p; + p += HBUINT16::static_size; + } + } + else + { + for (j = 0; j < run_count && i < count; j++, i++) + { + if (!check.in_range (p)) return false; + deltas[i] = *(const HBINT8 *)p++; + } + } + if (j < run_count) return false; + } + return true; + } + + private: + hb_blob_ptr_t gvar_table; + hb_vector_t shared_tuples; + glyf::accelerator_t glyf; + }; + protected: FixedVersion<> version; /* Version of gvar table. Set to 0x00010000u. */ HBUINT16 axisCount; From 21aaf30058823e06c121908734b05c6c7f0bdeec Mon Sep 17 00:00:00 2001 From: Michiharu Ariza Date: Fri, 1 Mar 2019 18:12:31 -0800 Subject: [PATCH 11/16] fix build attempt --- src/hb-ot-var-gvar-table.hh | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/hb-ot-var-gvar-table.hh b/src/hb-ot-var-gvar-table.hh index bba8f9b23..455d051dc 100644 --- a/src/hb-ot-var-gvar-table.hh +++ b/src/hb-ot-var-gvar-table.hh @@ -101,7 +101,7 @@ struct TupleVarHeader int start = start_tuple[i]; int end = end_tuple[i]; if (unlikely (start > peak || peak > end || - start < 0 && end > 0 && peak)) continue; + (start < 0 && end > 0 && peak))) continue; if (v < start || v > end) return 0.f; if (v < peak) { if (peak != start) scalar *= (float)(v - start) / (peak - start); } @@ -356,7 +356,7 @@ struct gvar memset (this, 0, sizeof (accelerator_t)); gvar_table = hb_sanitize_context_t ().reference_table (face); - glyf.init (face); + glyf_accel.init (face); hb_blob_ptr_t fvar_table = hb_sanitize_context_t ().reference_table (face); unsigned int axis_count = fvar_table->get_axis_count (); fvar_table.destroy (); @@ -374,7 +374,7 @@ struct gvar void fini () { gvar_table.destroy (); - glyf.fini (); + glyf_accel.fini (); } bool apply_deltas_to_points (hb_codepoint_t glyph, @@ -436,14 +436,14 @@ struct gvar { hb_vector_t points; hb_vector_t end_points; - if (!glyf.get_contour_points (glyph, true, points, end_points)) return false; + if (!glyf_accel.get_contour_points (glyph, true, points, end_points)) return false; if (!apply_deltas_to_points (glyph, coords, coord_count, points.as_array (), end_points.as_array ())) return false; for (unsigned int i = 0; i < pp_t::PHANTOM_COUNT; i++) phantoms[i] = points[points.length - pp_t::PHANTOM_COUNT + i]; glyf::CompositeGlyphHeader::Iterator composite; - if (!glyf.get_composite (glyph, &composite)) return true; /* simple glyph */ + if (!glyf_accel.get_composite (glyph, &composite)) return true; /* simple glyph */ do { /* TODO: support component scale/transformation */ @@ -574,7 +574,7 @@ struct gvar private: hb_blob_ptr_t gvar_table; hb_vector_t shared_tuples; - glyf::accelerator_t glyf; + glyf::accelerator_t glyf_accel; }; protected: From 9aa5805a44e883c3dcb09a43e952b8bf40016423 Mon Sep 17 00:00:00 2001 From: Michiharu Ariza Date: Fri, 1 Mar 2019 18:24:56 -0800 Subject: [PATCH 12/16] more build fixes --- src/hb-ot-glyf-table.hh | 2 +- src/hb-ot-var-gvar-table.hh | 13 ++++++------- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/hb-ot-glyf-table.hh b/src/hb-ot-glyf-table.hh index 028722c49..d86894b0f 100644 --- a/src/hb-ot-glyf-table.hh +++ b/src/hb-ot-glyf-table.hh @@ -359,7 +359,7 @@ struct glyf coord_setter.set (_points[i], v); } return true; - }; + } /* for a simple glyph, return contour end points, flags, along with coordinate points * for a composite glyph, return pseudo component points diff --git a/src/hb-ot-var-gvar-table.hh b/src/hb-ot-var-gvar-table.hh index 455d051dc..805c68c68 100644 --- a/src/hb-ot-var-gvar-table.hh +++ b/src/hb-ot-var-gvar-table.hh @@ -177,7 +177,7 @@ struct GlyphVarData return (index < var_data->tupleVarCount.get_count ()) && in_range (current_tuple) && current_tuple->get_size (axis_count); - }; + } bool move_to_next () { @@ -345,7 +345,6 @@ struct gvar const HBUINT16 *get_short_offset_array () const { return (const HBUINT16 *)&offsetZ; } typedef glyf::accelerator_t::contour_point_t contour_point_t; - typedef glyf::accelerator_t::phantom_point_index_t pp_t; typedef glyf::accelerator_t::range_checker_t range_checker_t; public: @@ -439,8 +438,8 @@ struct gvar if (!glyf_accel.get_contour_points (glyph, true, points, end_points)) return false; if (!apply_deltas_to_points (glyph, coords, coord_count, points.as_array (), end_points.as_array ())) return false; - for (unsigned int i = 0; i < pp_t::PHANTOM_COUNT; i++) - phantoms[i] = points[points.length - pp_t::PHANTOM_COUNT + i]; + for (unsigned int i = 0; i < glyf::accelerator_t::PHANTOM_COUNT; i++) + phantoms[i] = points[points.length - glyf::accelerator_t::PHANTOM_COUNT + i]; glyf::CompositeGlyphHeader::Iterator composite; if (!glyf_accel.get_composite (glyph, &composite)) return true; /* simple glyph */ @@ -462,15 +461,15 @@ struct gvar if (coord_count != gvar_table->axisCount) return advance; hb_vector_t points; - points.resize (pp_t::PHANTOM_COUNT); + points.resize (glyf::accelerator_t::PHANTOM_COUNT); if (!get_var_metrics (glyph, coords, coord_count, points)) return advance; if (vertical) - return -(points[pp_t::PHANTOM_BOTTOM].y - points[pp_t::PHANTOM_TOP].y); // is this sign correct? + return -(points[glyf::accelerator_t::PHANTOM_BOTTOM].y - points[glyf::accelerator_t::PHANTOM_TOP].y); // is this sign correct? else - return points[pp_t::PHANTOM_RIGHT].x - points[pp_t::PHANTOM_LEFT].x; + return points[glyf::accelerator_t::PHANTOM_RIGHT].x - points[glyf::accelerator_t::PHANTOM_LEFT].x; } protected: From ff60f34dd6b57401c8904a8808c1775ee09f4458 Mon Sep 17 00:00:00 2001 From: Michiharu Ariza Date: Fri, 1 Mar 2019 21:33:21 -0800 Subject: [PATCH 13/16] build fix attempt --- src/hb-ot-glyf-table.hh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hb-ot-glyf-table.hh b/src/hb-ot-glyf-table.hh index d86894b0f..bab06afbd 100644 --- a/src/hb-ot-glyf-table.hh +++ b/src/hb-ot-glyf-table.hh @@ -334,7 +334,7 @@ struct glyf hb_vector_t &_points /* IN/OUT */, const range_checker_t &checker) { - const T coord_setter; + T coord_setter; float v = 0; for (unsigned int i = 0; i < _points.length - PHANTOM_COUNT; i++) { From 2e1965e572f5643c56e5a0c11a8bf7d5cf68a483 Mon Sep 17 00:00:00 2001 From: Michiharu Ariza Date: Fri, 1 Mar 2019 21:49:04 -0800 Subject: [PATCH 14/16] minor edits --- src/hb-ot-cff-common.hh | 2 +- src/hb-ot-layout-common.hh | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/hb-ot-cff-common.hh b/src/hb-ot-cff-common.hh index 78e80b2fa..797effcea 100644 --- a/src/hb-ot-cff-common.hh +++ b/src/hb-ot-cff-common.hh @@ -104,7 +104,7 @@ struct CFFIndex else return min_size + calculate_offset_array_size (offSize, count) + dataSize; } - + bool serialize (hb_serialize_context_t *c, const CFFIndex &src) { TRACE_SERIALIZE (this); diff --git a/src/hb-ot-layout-common.hh b/src/hb-ot-layout-common.hh index 4771bf7b8..a82e2d95d 100644 --- a/src/hb-ot-layout-common.hh +++ b/src/hb-ot-layout-common.hh @@ -1587,6 +1587,7 @@ static inline void ClassDef_serialize (hb_serialize_context_t *c, hb_array_t klasses) { c->start_embed ()->serialize (c, glyphs, klasses); } + /* * Item Variation Store */ @@ -2241,6 +2242,7 @@ struct Device DEFINE_SIZE_UNION (6, b); }; + } /* namespace OT */ From f5a466389382183cbc009b66734e0fa339f168ff Mon Sep 17 00:00:00 2001 From: Michiharu Ariza Date: Fri, 1 Mar 2019 21:54:49 -0800 Subject: [PATCH 15/16] fix build --- src/hb-ot-var-gvar-table.hh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/hb-ot-var-gvar-table.hh b/src/hb-ot-var-gvar-table.hh index 805c68c68..f81b269ba 100644 --- a/src/hb-ot-var-gvar-table.hh +++ b/src/hb-ot-var-gvar-table.hh @@ -265,7 +265,7 @@ struct gvar unsigned int subset_data_size = 0; for (hb_codepoint_t gid = 0; gid < num_glyphs; gid++) { - unsigned int old_gid; + hb_codepoint_t old_gid; if (!c->plan->old_gid_for_new_gid (gid, &old_gid)) continue; subset_data_size += get_glyph_var_data_length (old_gid); } @@ -283,7 +283,7 @@ struct gvar unsigned int glyph_offset = 0; for (hb_codepoint_t gid = 0; gid < num_glyphs; gid++) { - unsigned int old_gid; + hb_codepoint_t old_gid; unsigned int length = c->plan->old_gid_for_new_gid (gid, &old_gid)? get_glyph_var_data_length (old_gid): 0; if (long_offset) From 99502b324dd6cb45d401bc5f6cc08d7a77677ba5 Mon Sep 17 00:00:00 2001 From: Michiharu Ariza Date: Tue, 12 Mar 2019 11:03:53 -0700 Subject: [PATCH 16/16] add gvar::get_extents --- src/hb-ot-face.hh | 1 + src/hb-ot-font.cc | 3 + src/hb-ot-glyf-table.hh | 21 +++-- src/hb-ot-var-gvar-table.hh | 164 ++++++++++++++++++++++++++++++++++-- 4 files changed, 175 insertions(+), 14 deletions(-) diff --git a/src/hb-ot-face.hh b/src/hb-ot-face.hh index 7f47ba6cb..9deb37798 100644 --- a/src/hb-ot-face.hh +++ b/src/hb-ot-face.hh @@ -72,6 +72,7 @@ HB_OT_TABLE(OT, fvar) \ HB_OT_TABLE(OT, avar) \ HB_OT_TABLE(OT, MVAR) \ + HB_OT_ACCELERATOR(OT, gvar) \ /* OpenType math. */ \ HB_OT_TABLE(OT, MATH) \ /* OpenType color fonts. */ \ diff --git a/src/hb-ot-font.cc b/src/hb-ot-font.cc index 94a9fdc47..742059ee7 100644 --- a/src/hb-ot-font.cc +++ b/src/hb-ot-font.cc @@ -42,6 +42,7 @@ #include "hb-ot-post-table.hh" #include "hb-ot-stat-table.hh" // Just so we compile it; unused otherwise. #include "hb-ot-vorg-table.hh" +#include "hb-ot-var-gvar-table.hh" #include "hb-ot-color-cbdt-table.hh" #include "hb-ot-color-sbix-table.hh" @@ -181,6 +182,8 @@ hb_ot_get_glyph_extents (hb_font_t *font, { const hb_ot_face_t *ot_face = (const hb_ot_face_t *) font_data; bool ret = ot_face->sbix->get_extents (font, glyph, extents); + if (!ret) + ret = ot_face->gvar->get_extents (font, glyph, extents); if (!ret) ret = ot_face->glyf->get_extents (glyph, extents); if (!ret) diff --git a/src/hb-ot-glyf-table.hh b/src/hb-ot-glyf-table.hh index bab06afbd..538577a1d 100644 --- a/src/hb-ot-glyf-table.hh +++ b/src/hb-ot-glyf-table.hh @@ -306,7 +306,7 @@ struct glyf bool in_range (const T *p) const { return ((const char *) p) >= table + start_offset - && ((const char *) (p + T::static_size)) <= table + end_offset; + && ((const char *) p + T::static_size) <= table + end_offset; } protected: @@ -366,9 +366,9 @@ struct glyf * in both cases points trailed with four phantom points */ bool get_contour_points (hb_codepoint_t glyph, - bool phantom_only, hb_vector_t &_points /* OUT */, - hb_vector_t &_end_points /* OUT */) const + hb_vector_t &_end_points /* OUT */, + const bool phantom_only=false) const { unsigned int num_points = 0; unsigned int start_offset, end_offset; @@ -377,8 +377,17 @@ struct glyf if (end_offset - start_offset < GlyphHeader::static_size) return false; + glyf::CompositeGlyphHeader::Iterator composite; + if (get_composite (glyph, &composite)) + { + /* For a composite glyph, add one pseudo point for each component */ + do { num_points++; } while (composite.move_to_next()); + _points.resize (num_points + PHANTOM_COUNT); + for (unsigned int i = 0; i < _points.length; i++) _points[i].init (); + return true; + } + const GlyphHeader &glyph_header = StructAtOffset (glyf_table, start_offset); - if (unlikely (glyph_header.numberOfContours < 0)) return false; int16_t num_contours = (int16_t) glyph_header.numberOfContours; const HBUINT16 *end_pts = &StructAfter (glyph_header); @@ -428,8 +437,8 @@ struct glyf } /* Read x & y coordinates */ - return (!read_points (p, _points, checker) && - !read_points (p, _points, checker)); + return (read_points (p, _points, checker) && + read_points (p, _points, checker)); } /* based on FontTools _g_l_y_f.py::trim */ diff --git a/src/hb-ot-var-gvar-table.hh b/src/hb-ot-var-gvar-table.hh index f81b269ba..5c157a1d5 100644 --- a/src/hb-ot-var-gvar-table.hh +++ b/src/hb-ot-var-gvar-table.hh @@ -31,6 +31,8 @@ #include "hb-ot-glyf-table.hh" #include "hb-ot-var-fvar-table.hh" +#include + /* * gvar -- Glyph Variation Table * https://docs.microsoft.com/en-us/typography/opentype/spec/gvar @@ -376,6 +378,24 @@ struct gvar glyf_accel.fini (); } + protected: + typedef glyf::accelerator_t glyf_acc_t; + + static float infer_delta (float target_val, float pre_val, float fol_val, + float pre_delta, float fol_delta) + { + if (pre_val == fol_val) + return (pre_delta == fol_delta)? pre_delta: 0.f; + else if (target_val <= MIN (pre_val, fol_val)) + return (pre_val < fol_val) ? pre_delta: fol_delta; + else if (target_val >= MAX (pre_val, fol_val)) + return (pre_val > fol_val)? pre_delta: fol_delta; + + /* linear interpolation */ + float r = (target_val - pre_val) / (fol_val - pre_val); + return (1.f - r) * pre_delta + r * fol_delta; + } + bool apply_deltas_to_points (hb_codepoint_t glyph, const int *coords, unsigned int coord_count, const hb_array_t points, @@ -391,6 +411,11 @@ struct gvar &iterator)) return false; + hb_vector_t deltas; /* flag is used to indicate referenced point */ + deltas.resize (points.length); + for (unsigned int i = 0; i < deltas.length; i++) + deltas[i].init (); + do { float scalar = iterator.current_tuple->calculate_scalar (coords, coord_count, shared_tuples.as_array ()); if (scalar == 0.f) continue; @@ -419,12 +444,49 @@ struct gvar for (unsigned int i = 0; i < num_deltas; i++) { unsigned int pt_index = apply_to_all? i: indices[i]; - points[pt_index].x += x_deltas[i] * scalar; - points[pt_index].y += y_deltas[i] * scalar; + deltas[pt_index].flag = 1; /* this point is referenced, i.e., explicit deltas specified */ + deltas[pt_index].x += x_deltas[i] * scalar; + deltas[pt_index].y += y_deltas[i] * scalar; } /* TODO: interpolate untouched points for glyph extents */ } while (iterator.move_to_next ()); + /* infer deltas for unreferenced points */ + unsigned int start_point = 0; + for (unsigned int c = 0; c < end_points.length; c++) + { + unsigned int end_point = end_points[c]; + for (unsigned int i = start_point; i < end_point; i++) + { + if (deltas[i].flag) continue; + /* search in both directions within the contour for a pair of referenced points */ + unsigned int pre; + for (pre = i;;) + { + if (pre-- <= start_point) pre = end_point; + if (pre == i || deltas[pre].flag) break; + } + if (pre == i) continue; /* no (preceeding) referenced point was found */ + unsigned int fol; + for (fol = i;;) + { + if (fol++ >= end_point) fol = start_point; + if (fol == i || deltas[fol].flag) break; + } + assert (fol != i); + deltas[i].x = infer_delta (points[i].x, points[pre].x, points[fol].x, deltas[pre].x, deltas[fol].x); + deltas[i].y = infer_delta (points[i].y, points[pre].y, points[fol].y, deltas[pre].y, deltas[fol].y); + } + start_point = end_point + 1; + } + + /* apply accumulated / inferred deltas to points */ + for (unsigned int i = 0; i < points.length; i++) + { + points[i].x += deltas[i].x; + points[i].y += deltas[i].y; + } + return true; } @@ -435,11 +497,11 @@ struct gvar { hb_vector_t points; hb_vector_t end_points; - if (!glyf_accel.get_contour_points (glyph, true, points, end_points)) return false; + if (!glyf_accel.get_contour_points (glyph, points, end_points, true/*phantom_only*/)) return false; if (!apply_deltas_to_points (glyph, coords, coord_count, points.as_array (), end_points.as_array ())) return false; - for (unsigned int i = 0; i < glyf::accelerator_t::PHANTOM_COUNT; i++) - phantoms[i] = points[points.length - glyf::accelerator_t::PHANTOM_COUNT + i]; + for (unsigned int i = 0; i < glyf_acc_t::PHANTOM_COUNT; i++) + phantoms[i] = points[points.length - glyf_acc_t::PHANTOM_COUNT + i]; glyf::CompositeGlyphHeader::Iterator composite; if (!glyf_accel.get_composite (glyph, &composite)) return true; /* simple glyph */ @@ -453,6 +515,57 @@ struct gvar return true; } + struct bounds_t + { + bounds_t () { min.x = min.y = FLT_MAX; max.x = max.y = FLT_MIN; } + + void add (const contour_point_t &p) + { + min.x = MIN (min.x, p.x); + min.y = MIN (min.y, p.y); + max.x = MAX (max.x, p.x); + max.y = MAX (max.y, p.y); + } + + void _union (const bounds_t &b) + { add (b.min); add (b.max); } + + contour_point_t min; + contour_point_t max; + }; + + /* Note: Recursively calls itself. Who's checking recursively nested composite glyph BTW? */ + bool get_bounds_var (hb_codepoint_t glyph, + const int *coords, unsigned int coord_count, + bounds_t &bounds) const + { + hb_vector_t points; + hb_vector_t end_points; + if (!glyf_accel.get_contour_points (glyph, points, end_points)) return false; + if (!apply_deltas_to_points (glyph, coords, coord_count, points.as_array (), end_points.as_array ())) return false; + + glyf::CompositeGlyphHeader::Iterator composite; + if (!glyf_accel.get_composite (glyph, &composite)) + { + /* simple glyph */ + for (unsigned int i = 0; i + glyf_acc_t::PHANTOM_COUNT < points.length; i++) + bounds.add (points[i]); + return true; + } + /* composite glyph */ + do + { + bounds_t comp_bounds; + if (!get_bounds_var (composite.current->glyphIndex, coords, coord_count, comp_bounds)) + return false; + + /* TODO: support component scale/transformation */ + bounds._union (comp_bounds); + } while (composite.move_to_next()); + return true; + } + + public: float get_advance_var (hb_codepoint_t glyph, const int *coords, unsigned int coord_count, bool vertical) const @@ -461,15 +574,48 @@ struct gvar if (coord_count != gvar_table->axisCount) return advance; hb_vector_t points; - points.resize (glyf::accelerator_t::PHANTOM_COUNT); + points.resize (glyf_acc_t::PHANTOM_COUNT); if (!get_var_metrics (glyph, coords, coord_count, points)) return advance; if (vertical) - return -(points[glyf::accelerator_t::PHANTOM_BOTTOM].y - points[glyf::accelerator_t::PHANTOM_TOP].y); // is this sign correct? + return -(points[glyf_acc_t::PHANTOM_BOTTOM].y - points[glyf_acc_t::PHANTOM_TOP].y); // is this sign correct? else - return points[glyf::accelerator_t::PHANTOM_RIGHT].x - points[glyf::accelerator_t::PHANTOM_LEFT].x; + return points[glyf_acc_t::PHANTOM_RIGHT].x - points[glyf_acc_t::PHANTOM_LEFT].x; + } + + bool get_extents (hb_font_t *font, hb_codepoint_t glyph, + hb_glyph_extents_t *extents) const + { + unsigned int coord_count; + const int *coords = hb_font_get_var_coords_normalized (font, &coord_count); + if (!coords || coord_count != gvar_table->axisCount) return false; /* fallback on glyf */ + + bounds_t bounds; + if (unlikely (!get_bounds_var (glyph, coords, coord_count, bounds))) return false; + + if (bounds.min.x >= bounds.max.x) + { + extents->width = 0; + extents->x_bearing = 0; + } + else + { + extents->x_bearing = (int32_t)floorf (bounds.min.x); + extents->width = (int32_t)ceilf (bounds.max.x) - extents->x_bearing; + } + if (bounds.min.y >= bounds.max.y) + { + extents->height = 0; + extents->y_bearing = 0; + } + else + { + extents->y_bearing = (int32_t)ceilf (bounds.max.y); + extents->height = (int32_t)floorf (bounds.min.y) - extents->y_bearing; + } + return true; } protected: @@ -590,6 +736,8 @@ struct gvar DEFINE_SIZE_MIN (20); }; +struct gvar_accelerator_t : gvar::accelerator_t {}; + } /* namespace OT */ #endif /* HB_OT_VAR_GVAR_TABLE_HH */