From a190140fa47ff5655edbb4eb414175852f722f85 Mon Sep 17 00:00:00 2001 From: Michiharu Ariza Date: Thu, 21 Feb 2019 15:47:27 -0800 Subject: [PATCH 001/101] 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 002/101] 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 003/101] 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 004/101] 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 005/101] 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 006/101] 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 d66f7e14a0097d8ca54ad9824f7aa7daee6c7f72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=ABl=20R=2E=20Langlois?= Date: Mon, 25 Feb 2019 15:26:58 -0500 Subject: [PATCH 007/101] Remove Forcing Diagnostic Colours from CMakeLists.txt (#1597) Fixes https://github.com/harfbuzz/harfbuzz/issues/1596 --- CMakeLists.txt | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 6a1a36dfb..f64f96d1c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -842,18 +842,6 @@ if (NOT SKIP_INSTALL_LIBRARIES AND NOT SKIP_INSTALL_ALL) endif () endif () -if (UNIX AND CMAKE_GENERATOR STREQUAL "Ninja") - if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang") - set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fcolor-diagnostics") - set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fcolor-diagnostics") - endif () - if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") - set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fdiagnostics-color") - set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fdiagnostics-color") - endif () -endif () - - if (HB_BUILD_TESTS) ## src/ executables foreach (prog main test test-would-substitute test-size-params test-buffer-serialize hb-ot-tag test-unicode-ranges) From 45adc185260f0fa1fa86472aafb7f91f942c567e Mon Sep 17 00:00:00 2001 From: David Corbett Date: Mon, 18 Feb 2019 22:30:40 -0500 Subject: [PATCH 008/101] Fix or document unsupported font-feature-settings --- src/hb-common.cc | 18 ++++++++++++------ util/options.cc | 3 ++- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/src/hb-common.cc b/src/hb-common.cc index fb8c7b72f..ab93bf427 100644 --- a/src/hb-common.cc +++ b/src/hb-common.cc @@ -731,7 +731,7 @@ parse_uint (const char **pp, const char *end, unsigned int *pv) /* Intentionally use strtol instead of strtoul, such that * -1 turns into "big number"... */ errno = 0; - v = strtol (p, &pend, 0); + v = strtol (p, &pend, 10); if (errno || p == pend) return false; @@ -755,7 +755,7 @@ parse_uint32 (const char **pp, const char *end, uint32_t *pv) /* Intentionally use strtol instead of strtoul, such that * -1 turns into "big number"... */ errno = 0; - v = strtol (p, &pend, 0); + v = strtol (p, &pend, 10); if (errno || p == pend) return false; @@ -857,9 +857,14 @@ parse_bool (const char **pp, const char *end, uint32_t *pv) (*pp)++; /* CSS allows on/off as aliases 1/0. */ - if (*pp - p == 2 && 0 == strncmp (p, "on", 2)) + if (*pp - p == 2 + && TOLOWER (p[0]) == 'o' + && TOLOWER (p[1]) == 'n') *pv = 1; - else if (*pp - p == 3 && 0 == strncmp (p, "off", 3)) + else if (*pp - p == 3 + && TOLOWER (p[0]) == 'o' + && TOLOWER (p[1]) == 'f' + && TOLOWER (p[2]) == 'f') *pv = 0; else return false; @@ -975,8 +980,9 @@ parse_one_feature (const char **pp, const char *end, hb_feature_t *feature) * Parses a string into a #hb_feature_t. * * The format for specifying feature strings follows. All valid CSS - * font-feature-settings values other than 'normal' and 'inherited' are also - * accepted, though, not documented below. + * font-feature-settings values other than 'normal' and the global values are + * also accepted, though not documented below. CSS string escapes are not + * supported. * * The range indices refer to the positions between Unicode characters. The * position before the first character is always 0. diff --git a/util/options.cc b/util/options.cc index b315c6a75..c5a4f0f0b 100644 --- a/util/options.cc +++ b/util/options.cc @@ -432,7 +432,8 @@ shape_options_t::add_options (option_parser_t *parser) " Features can be enabled or disabled, either globally or limited to\n" " specific character ranges. The format for specifying feature settings\n" " follows. All valid CSS font-feature-settings values other than 'normal'\n" - " and 'inherited' are also accepted, though, not documented below.\n" + " and the global values are also accepted, though not documented below.\n" + " CSS string escapes are not supported." "\n" " The range indices refer to the positions between Unicode characters,\n" " unless the --utf8-clusters is provided, in which case range indices\n" From 6f91e0d903d7510a4612a9cc7306ec04260cefed Mon Sep 17 00:00:00 2001 From: Michiharu Ariza Date: Tue, 26 Feb 2019 11:11:50 -0800 Subject: [PATCH 009/101] 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 45149eb34f9735b5d690a2a7956adb42b938c8d9 Mon Sep 17 00:00:00 2001 From: Ebrahim Byagowi Date: Fri, 22 Feb 2019 13:13:42 +0330 Subject: [PATCH 010/101] [dwrite] hb_directwrite_face_create, a new API It makes a hb_face_t from IDWriteFontFace, useful when using DirectWrite facilities for font selection, loading and rendering but using harfbuzz for shaping. --- src/hb-directwrite.cc | 70 +++++++++++++++++++++++++++++++++++++++---- src/hb-directwrite.h | 5 +++- 2 files changed, 69 insertions(+), 6 deletions(-) diff --git a/src/hb-directwrite.cc b/src/hb-directwrite.cc index aaf10a1d6..137cd56c8 100644 --- a/src/hb-directwrite.cc +++ b/src/hb-directwrite.cc @@ -1,5 +1,5 @@ /* - * Copyright © 2015-2018 Ebrahim Byagowi + * Copyright © 2015-2019 Ebrahim Byagowi * * This is part of HarfBuzz, a text shaping library. * @@ -658,10 +658,10 @@ retry_getglyphs: * alignment needed after the WORD array. sizeof (WORD) == 2. */ unsigned int glyphs_size = (scratch_size * sizeof (int) - 2) / (sizeof (WORD) + - sizeof (DWRITE_SHAPING_GLYPH_PROPERTIES) + - sizeof (int) + - sizeof (DWRITE_GLYPH_OFFSET) + - sizeof (uint32_t)); + sizeof (DWRITE_SHAPING_GLYPH_PROPERTIES) + + sizeof (int) + + sizeof (DWRITE_GLYPH_OFFSET) + + sizeof (uint32_t)); ALLOCATE_ARRAY (uint32_t, vis_clusters, glyphs_size); #undef ALLOCATE_ARRAY @@ -868,3 +868,63 @@ hb_directwrite_shape_experimental_width (hb_font_t *font, return res; } + +struct _hb_directwrite_font_table_context { + IDWriteFontFace *face; + void *table_context; +}; + +static void +_hb_directwrite_table_data_release (void *data) +{ + _hb_directwrite_font_table_context *context = (_hb_directwrite_font_table_context *) data; + context->face->ReleaseFontTable (context->table_context); + delete context; +} + +static hb_blob_t * +reference_table (hb_face_t *face HB_UNUSED, hb_tag_t tag, void *user_data) +{ + IDWriteFontFace *dw_face = ((IDWriteFontFace *) user_data); + const void *data; + uint32_t length; + void *table_context; + BOOL exists; + if (!dw_face || FAILED (dw_face->TryGetFontTable (hb_uint32_swap (tag), &data, + &length, &table_context, &exists))) + return nullptr; + + if (!data || !exists || !length) + { + dw_face->ReleaseFontTable (table_context); + return nullptr; + } + + _hb_directwrite_font_table_context *context = new _hb_directwrite_font_table_context; + context->face = dw_face; + context->table_context = table_context; + + return hb_blob_create ((const char *) data, length, HB_MEMORY_MODE_READONLY, + context, _hb_directwrite_table_data_release); +} + +static void +_hb_directwrite_font_release (void *data) +{ + if (data) + ((IDWriteFontFace *) data)->Release (); +} + +/** + * hb_directwrite_face_create: + * @font_face: + * Since: REPLACEME + **/ +hb_face_t * +hb_directwrite_face_create (IDWriteFontFace *font_face) +{ + if (font_face) + font_face->AddRef (); + return hb_face_create_for_tables (reference_table, font_face, + _hb_directwrite_font_release); +} diff --git a/src/hb-directwrite.h b/src/hb-directwrite.h index 9bfd1f727..09776fd09 100644 --- a/src/hb-directwrite.h +++ b/src/hb-directwrite.h @@ -1,5 +1,5 @@ /* - * Copyright © 2015 Ebrahim Byagowi + * Copyright © 2015-2019 Ebrahim Byagowi * * This is part of HarfBuzz, a text shaping library. * @@ -34,6 +34,9 @@ hb_directwrite_shape_experimental_width (hb_font_t *font, hb_buffer_t *buffer, const hb_feature_t *features, unsigned int num_features, float width); +HB_EXTERN hb_face_t * +hb_directwrite_face_create (IDWriteFontFace *font_face); + HB_END_DECLS #endif /* HB_DIRECTWRITE_H */ From 33354ab6b87211ae2e702bee162fa4260078e70a Mon Sep 17 00:00:00 2001 From: Michiharu Ariza Date: Tue, 26 Feb 2019 16:54:00 -0800 Subject: [PATCH 011/101] 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 4f37ab63de9705d7bf74ee75364747e41b7c06a1 Mon Sep 17 00:00:00 2001 From: Garret Rieger Date: Thu, 28 Feb 2019 17:25:05 -0800 Subject: [PATCH 012/101] Make hb_subset_input_glyph_set () actually do something. --- src/hb-subset-plan.cc | 3 +++ test/api/hb-subset-test.h | 11 ++++++++++- test/api/test-subset-glyf.c | 24 ++++++++++++++++++++++++ 3 files changed, 37 insertions(+), 1 deletion(-) diff --git a/src/hb-subset-plan.cc b/src/hb-subset-plan.cc index 5702d0173..49ab9e133 100644 --- a/src/hb-subset-plan.cc +++ b/src/hb-subset-plan.cc @@ -96,6 +96,7 @@ _remove_invalid_gids (hb_set_t *glyphs, static hb_set_t * _populate_gids_to_retain (hb_face_t *face, const hb_set_t *unicodes, + const hb_set_t *input_glyphs_to_retain, bool close_over_gsub, hb_set_t *unicodes_to_retain, hb_map_t *codepoint_to_glyph, @@ -110,6 +111,7 @@ _populate_gids_to_retain (hb_face_t *face, hb_set_t *initial_gids_to_retain = hb_set_create (); initial_gids_to_retain->add (0); // Not-def + hb_set_union (initial_gids_to_retain, input_glyphs_to_retain); hb_codepoint_t cp = HB_SET_VALUE_INVALID; while (unicodes->next (&cp)) @@ -213,6 +215,7 @@ hb_subset_plan_create (hb_face_t *face, plan->reverse_glyph_map = hb_map_create(); plan->_glyphset = _populate_gids_to_retain (face, input->unicodes, + input->glyphs, !plan->drop_layout, plan->unicodes, plan->codepoint_to_glyph, diff --git a/test/api/hb-subset-test.h b/test/api/hb-subset-test.h index cefa4e066..3e759a8a9 100644 --- a/test/api/hb-subset-test.h +++ b/test/api/hb-subset-test.h @@ -48,7 +48,7 @@ typedef short bool; HB_BEGIN_DECLS static inline hb_subset_input_t * -hb_subset_test_create_input(const hb_set_t *codepoints) +hb_subset_test_create_input (const hb_set_t *codepoints) { hb_subset_input_t *input = hb_subset_input_create_or_fail (); hb_set_t * input_codepoints = hb_subset_input_unicode_set (input); @@ -56,6 +56,15 @@ hb_subset_test_create_input(const hb_set_t *codepoints) return input; } +static inline hb_subset_input_t * +hb_subset_test_create_input_from_glyphs (const hb_set_t *glyphs) +{ + hb_subset_input_t *input = hb_subset_input_create_or_fail (); + hb_set_t * input_glyphs = hb_subset_input_glyph_set (input); + hb_set_union (input_glyphs, glyphs); + return input; +} + static inline hb_face_t * hb_subset_test_create_subset (hb_face_t *source, hb_subset_input_t *input) diff --git a/test/api/test-subset-glyf.c b/test/api/test-subset-glyf.c index e8609ca83..4671156f9 100644 --- a/test/api/test-subset-glyf.c +++ b/test/api/test-subset-glyf.c @@ -79,6 +79,29 @@ test_subset_glyf (void) hb_face_destroy (face_ac); } +static void +test_subset_glyf_with_input_glyphs (void) +{ + hb_face_t *face_abc = hb_test_open_font_file ("fonts/Roboto-Regular.abc.ttf"); + hb_face_t *face_ac = hb_test_open_font_file ("fonts/Roboto-Regular.ac.ttf"); + + hb_set_t *glyphs = hb_set_create(); + hb_face_t *face_abc_subset; + hb_set_add (glyphs, 1); + hb_set_add (glyphs, 3); + face_abc_subset = + hb_subset_test_create_subset (face_abc, hb_subset_test_create_input_from_glyphs (glyphs)); + hb_set_destroy (glyphs); + + hb_subset_test_check (face_ac, face_abc_subset, HB_TAG ('g','l','y','f')); + hb_subset_test_check (face_ac, face_abc_subset, HB_TAG ('l','o','c', 'a')); + check_maxp_num_glyphs(face_abc_subset, 3, true); + + hb_face_destroy (face_abc_subset); + hb_face_destroy (face_abc); + hb_face_destroy (face_ac); +} + static void test_subset_glyf_with_components (void) { @@ -291,6 +314,7 @@ main (int argc, char **argv) hb_test_add (test_subset_glyf_noop); hb_test_add (test_subset_glyf); + hb_test_add (test_subset_glyf_with_input_glyphs); hb_test_add (test_subset_glyf_strip_hints_simple); hb_test_add (test_subset_glyf_strip_hints_composite); hb_test_add (test_subset_glyf_strip_hints_invalid); From fa2b3d30f6562a7457acca205f1bf544089c88ba Mon Sep 17 00:00:00 2001 From: Michiharu Ariza Date: Fri, 1 Mar 2019 15:14:22 -0800 Subject: [PATCH 013/101] 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 8a25868e6a41a3d82782aadb3c7b744ad87d20ff Mon Sep 17 00:00:00 2001 From: Ebrahim Byagowi Date: Sat, 2 Mar 2019 03:24:49 +0330 Subject: [PATCH 014/101] Minor, remove .editorconfig hack As vscode is going to support it soon --- .editorconfig | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/.editorconfig b/.editorconfig index 0c93e6a64..499147351 100644 --- a/.editorconfig +++ b/.editorconfig @@ -8,11 +8,8 @@ insert_final_newline = true [*.{c,cc,h,hh}] tab_width = 8 -indent_style = tab -# This should be the following but as VSCode and Atom don't support it, disabled for now -# https://github.com/Microsoft/vscode/issues/44438 -# indent_size = 2 -# indent_style = space +indent_size = 2 +indent_style = space [*.{py,sh}] indent_style = tab From 21aaf30058823e06c121908734b05c6c7f0bdeec Mon Sep 17 00:00:00 2001 From: Michiharu Ariza Date: Fri, 1 Mar 2019 18:12:31 -0800 Subject: [PATCH 015/101] 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 016/101] 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 017/101] 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 018/101] 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 019/101] 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 731b13e4e9190a45e51f855f19e88869a7718d43 Mon Sep 17 00:00:00 2001 From: Martin Hosken Date: Mon, 4 Mar 2019 11:12:21 +0700 Subject: [PATCH 020/101] Fix offset drift in graphite integration --- src/hb-graphite2.cc | 35 +++++++++++++++++++---------------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/src/hb-graphite2.cc b/src/hb-graphite2.cc index a1c602c29..fdb545342 100644 --- a/src/hb-graphite2.cc +++ b/src/hb-graphite2.cc @@ -225,7 +225,7 @@ struct hb_graphite2_cluster_t { unsigned int base_glyph; unsigned int num_glyphs; unsigned int cluster; - float advance; + unsigned int advance; }; hb_bool_t @@ -253,7 +253,7 @@ _hb_graphite2_shape (hb_shape_plan_t *shape_plan HB_UNUSED, gr_segment *seg = nullptr; const gr_slot *is; unsigned int ci = 0, ic = 0; - float curradvx = 0., curradvy = 0.; + unsigned int curradvx = 0, curradvy = 0; unsigned int scratch_size; hb_buffer_t::scratch_buffer_t *scratch = buffer->get_scratch_buffer (&scratch_size); @@ -324,11 +324,15 @@ _hb_graphite2_shape (hb_shape_plan_t *shape_plan HB_UNUSED, hb_codepoint_t *pg = gids; clusters[0].cluster = buffer->info[0].cluster; - float curradv = 0.; + unsigned int upem = hb_face_get_upem (face); + float xscale = (float) font->x_scale / upem; + float yscale = (float) font->y_scale / upem; + yscale *= yscale / xscale; + unsigned int curradv = 0; if (HB_DIRECTION_IS_BACKWARD(buffer->props.direction)) { - curradv = gr_slot_origin_X(gr_seg_first_slot(seg)); - clusters[0].advance = gr_seg_advance_X(seg) - curradv; + curradv = gr_slot_origin_X(gr_seg_first_slot(seg)) * xscale; + clusters[0].advance = gr_seg_advance_X(seg) * xscale - curradv; } else clusters[0].advance = 0; @@ -355,14 +359,17 @@ _hb_graphite2_shape (hb_shape_plan_t *shape_plan HB_UNUSED, c->base_glyph = ic; c->num_glyphs = 0; if (HB_DIRECTION_IS_BACKWARD(buffer->props.direction)) - c->advance = curradv - gr_slot_origin_X(is); + { + c->advance = curradv - gr_slot_origin_X(is) * xscale; + curradv -= c->advance; + } else { c->advance = 0; - clusters[ci].advance += gr_slot_origin_X(is) - curradv; + clusters[ci].advance += gr_slot_origin_X(is) * xscale - curradv; + curradv += clusters[ci].advance; } ci++; - curradv = gr_slot_origin_X(is); } clusters[ci].num_glyphs++; @@ -373,7 +380,7 @@ _hb_graphite2_shape (hb_shape_plan_t *shape_plan HB_UNUSED, if (HB_DIRECTION_IS_BACKWARD(buffer->props.direction)) clusters[ci].advance += curradv; else - clusters[ci].advance += gr_seg_advance_X(seg) - curradv; + clusters[ci].advance += gr_seg_advance_X(seg) * xscale - curradv; ci++; for (unsigned int i = 0; i < ci; ++i) @@ -388,10 +395,6 @@ _hb_graphite2_shape (hb_shape_plan_t *shape_plan HB_UNUSED, } buffer->len = glyph_count; - unsigned int upem = hb_face_get_upem (face); - float xscale = (float) font->x_scale / upem; - float yscale = (float) font->y_scale / upem; - yscale *= yscale / xscale; /* Positioning. */ unsigned int currclus = (unsigned int) -1; const hb_glyph_info_t *info = buffer->info; @@ -404,7 +407,7 @@ _hb_graphite2_shape (hb_shape_plan_t *shape_plan HB_UNUSED, pPos->x_offset = gr_slot_origin_X (is) * xscale - curradvx; pPos->y_offset = gr_slot_origin_Y (is) * yscale - curradvy; if (info->cluster != currclus) { - pPos->x_advance = info->var1.i32 * xscale; + pPos->x_advance = info->var1.i32; curradvx += pPos->x_advance; currclus = info->cluster; } else @@ -421,7 +424,7 @@ _hb_graphite2_shape (hb_shape_plan_t *shape_plan HB_UNUSED, { if (info->cluster != currclus) { - pPos->x_advance = info->var1.i32 * xscale; + pPos->x_advance = info->var1.i32; curradvx -= pPos->x_advance; currclus = info->cluster; } else @@ -429,7 +432,7 @@ _hb_graphite2_shape (hb_shape_plan_t *shape_plan HB_UNUSED, pPos->y_advance = gr_slot_advance_Y (is, grface, nullptr) * yscale; curradvy -= pPos->y_advance; - pPos->x_offset = (gr_slot_origin_X (is) - info->var1.i32) * xscale - curradvx + pPos->x_advance; + pPos->x_offset = gr_slot_origin_X (is) * xscale - info->var1.i32 - curradvx + pPos->x_advance; pPos->y_offset = gr_slot_origin_Y (is) * yscale - curradvy; } hb_buffer_reverse_clusters (buffer); From d936ad4582a0017cf88406372d7c08b9896beed6 Mon Sep 17 00:00:00 2001 From: Stephan Bergmann Date: Tue, 5 Mar 2019 17:18:57 +0100 Subject: [PATCH 021/101] Fix hb_atomic_* variants based on C++11 atomics I stumbled over this when trying to upgrade the version of HarfBuzz used by LibreOffice to 3.2.1 (see "Upgrade to latest HarfBuzz 2.3.1"), where building with MSVC 2017 failed like > c:\cygwin\home\tdf\lode\jenkins\workspace\gerrit_windows\workdir\unpackedtarball\harfbuzz\src\hb-atomic.hh(272): error C2440: 'reinterpret_cast': cannot convert from 'const int *' to 'std::atomic *' > c:\cygwin\home\tdf\lode\jenkins\workspace\gerrit_windows\workdir\unpackedtarball\harfbuzz\src\hb-atomic.hh(272): note: Conversion loses qualifiers > c:\cygwin\home\tdf\lode\jenkins\workspace\gerrit_windows\workdir\unpackedtarball\harfbuzz\src\hb-atomic.hh(272): error C2227: left of '->load' must point to class/struct/union/generic type (see ). I added all the necessary "const" to make building of HarfBuzz 2.3.1 with MSVC 2017 succeed for me. There may be more missing at least conceptually. --- src/hb-atomic.hh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/hb-atomic.hh b/src/hb-atomic.hh index d98a9605e..f9afd4fff 100644 --- a/src/hb-atomic.hh +++ b/src/hb-atomic.hh @@ -85,11 +85,11 @@ _hb_atomic_ptr_impl_cmplexch (const void **P, const void *O_, const void *N) #define hb_atomic_int_impl_add(AI, V) (reinterpret_cast *> (AI)->fetch_add ((V), std::memory_order_acq_rel)) #define hb_atomic_int_impl_set_relaxed(AI, V) (reinterpret_cast *> (AI)->store ((V), std::memory_order_relaxed)) #define hb_atomic_int_impl_set(AI, V) (reinterpret_cast *> (AI)->store ((V), std::memory_order_release)) -#define hb_atomic_int_impl_get_relaxed(AI) (reinterpret_cast *> (AI)->load (std::memory_order_relaxed)) -#define hb_atomic_int_impl_get(AI) (reinterpret_cast *> (AI)->load (std::memory_order_acquire)) +#define hb_atomic_int_impl_get_relaxed(AI) (reinterpret_cast const *> (AI)->load (std::memory_order_relaxed)) +#define hb_atomic_int_impl_get(AI) (reinterpret_cast const *> (AI)->load (std::memory_order_acquire)) #define hb_atomic_ptr_impl_set_relaxed(P, V) (reinterpret_cast *> (P)->store ((V), std::memory_order_relaxed)) -#define hb_atomic_ptr_impl_get_relaxed(P) (reinterpret_cast *> (P)->load (std::memory_order_relaxed)) +#define hb_atomic_ptr_impl_get_relaxed(P) (reinterpret_cast const *> (P)->load (std::memory_order_relaxed)) #define hb_atomic_ptr_impl_get(P) (reinterpret_cast *> (P)->load (std::memory_order_acquire)) static inline bool _hb_atomic_ptr_impl_cmplexch (const void **P, const void *O_, const void *N) From 2f125b0fa763c3be7d8d74489c027f7155607756 Mon Sep 17 00:00:00 2001 From: Adrian Wong Date: Wed, 13 Feb 2019 21:04:46 +1100 Subject: [PATCH 022/101] [indic] Remove superfluous ZWNJ check in final reorder of pre-base matras --- src/hb-ot-shape-complex-indic.cc | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/hb-ot-shape-complex-indic.cc b/src/hb-ot-shape-complex-indic.cc index d2d0a5a34..64e7dcff3 100644 --- a/src/hb-ot-shape-complex-indic.cc +++ b/src/hb-ot-shape-complex-indic.cc @@ -1199,9 +1199,14 @@ final_reordering_syllable (const hb_ot_shape_plan_t *plan, goto search; } } - /* -> If ZWNJ follows this halant, position is moved after it. */ - if (info[new_pos + 1].indic_category() == OT_ZWNJ) - new_pos++; + /* -> If ZWNJ follows this halant, position is moved after it. + * + * IMPLEMENTATION NOTES: + * + * This is taken care of by the state-machine. A Halant,ZWNJ is a terminating + * sequence for a consonant syllable; any pre-base matras occurring after it + * will belong to the subsequent syllable. + */ } } else From e723c04de1b3dcd96e6a70baf09e3ae2ddbbc0bf Mon Sep 17 00:00:00 2001 From: David Corbett Date: Wed, 6 Mar 2019 12:37:25 -0500 Subject: [PATCH 023/101] Update to Unicode 12.0.0 --- src/gen-use-table.py | 1 - src/gen-vowel-constraints.py | 1 + src/hb-common.h | 8 + src/hb-ot-shape-complex-arabic-table.hh | 14 +- src/hb-ot-shape-complex-indic-table.cc | 56 +- src/hb-ot-shape-complex-use-table.cc | 49 +- src/hb-ot-shape-complex-vowel-constraints.cc | 4 +- src/hb-ot-shape-complex.hh | 3 + src/hb-ot-tag-table.hh | 12 +- src/hb-ucdn.cc | 4 + src/hb-ucdn/ucdn.h | 11 + src/hb-ucdn/ucdn_db.h | 3002 +++++++++--------- src/hb-unicode-emoji-table.hh | 6 +- 13 files changed, 1633 insertions(+), 1538 deletions(-) diff --git a/src/gen-use-table.py b/src/gen-use-table.py index be204b60b..2631c46ff 100755 --- a/src/gen-use-table.py +++ b/src/gen-use-table.py @@ -384,7 +384,6 @@ def map_to_use(data): if U == 0x11302: UIPC = Top if U == 0x1133C: UIPC = Bottom if U == 0x1171E: UIPC = Left # Correct?! - if 0x1CF2 <= U <= 0x1CF3: UIPC = Right if 0x1CF8 <= U <= 0x1CF9: UIPC = Top # https://github.com/roozbehp/unicode-data/issues/8 if U == 0x0A51: UIPC = Bottom diff --git a/src/gen-vowel-constraints.py b/src/gen-vowel-constraints.py index b7f6be223..e4dc95dfe 100755 --- a/src/gen-vowel-constraints.py +++ b/src/gen-vowel-constraints.py @@ -212,6 +212,7 @@ print (' if (processed)') print (' {') print (' if (buffer->idx < count)') print (' buffer->next_glyph ();') +print (' buffer->swap_buffers ();') print (' }') print ('}') diff --git a/src/hb-common.h b/src/hb-common.h index 2b29e4441..fbabd71c8 100644 --- a/src/hb-common.h +++ b/src/hb-common.h @@ -357,6 +357,14 @@ typedef enum /*11.0*/HB_SCRIPT_OLD_SOGDIAN = HB_TAG ('S','o','g','o'), /*11.0*/HB_SCRIPT_SOGDIAN = HB_TAG ('S','o','g','d'), + /* + * Since REPLACEME + */ + /*12.0*/HB_SCRIPT_ELYMAIC = HB_TAG ('E','l','y','m'), + /*12.0*/HB_SCRIPT_NANDINAGARI = HB_TAG ('N','a','n','d'), + /*12.0*/HB_SCRIPT_NYIAKENG_PUACHUE_HMONG = HB_TAG ('H','m','n','p'), + /*12.0*/HB_SCRIPT_WANCHO = HB_TAG ('W','c','h','o'), + /* No script set. */ HB_SCRIPT_INVALID = HB_TAG_NONE, diff --git a/src/hb-ot-shape-complex-arabic-table.hh b/src/hb-ot-shape-complex-arabic-table.hh index 9459aad3d..719fabd35 100644 --- a/src/hb-ot-shape-complex-arabic-table.hh +++ b/src/hb-ot-shape-complex-arabic-table.hh @@ -6,10 +6,10 @@ * * on files with these headers: * - * # ArabicShaping-11.0.0.txt - * # Date: 2018-02-21, 14:50:00 GMT [KW, RP] - * # Blocks-11.0.0.txt - * # Date: 2017-10-16, 24:39:00 GMT [KW] + * # ArabicShaping-12.0.0.txt + * # Date: 2018-09-22, 23:54:00 GMT [KW, RP] + * # Blocks-12.0.0.txt + * # Date: 2018-07-30, 19:40:00 GMT [KW] * UnicodeData.txt does not have a header. */ @@ -152,9 +152,9 @@ static const uint8_t joining_table[] = /* 1E900 */ D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D, /* 1E920 */ D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D, - /* 1E940 */ D,D,D,D, + /* 1E940 */ D,D,D,D,X,X,X,X,X,X,X,T, -}; /* Table items: 1304; occupancy: 56% */ +}; /* Table items: 1312; occupancy: 56% */ static unsigned int @@ -190,7 +190,7 @@ joining_type (hb_codepoint_t u) break; case 0x1Eu: - if (hb_in_range (u, 0x1E900u, 0x1E943u)) return joining_table[u - 0x1E900u + joining_offset_0x1e900u]; + if (hb_in_range (u, 0x1E900u, 0x1E94Bu)) return joining_table[u - 0x1E900u + joining_offset_0x1e900u]; break; default: diff --git a/src/hb-ot-shape-complex-indic-table.cc b/src/hb-ot-shape-complex-indic-table.cc index 43b5ef8b7..d26bbb8f3 100644 --- a/src/hb-ot-shape-complex-indic-table.cc +++ b/src/hb-ot-shape-complex-indic-table.cc @@ -6,12 +6,12 @@ * * on files with these headers: * - * # IndicSyllabicCategory-11.0.0.txt - * # Date: 2018-05-21, 18:33:00 GMT [KW, RP] - * # IndicPositionalCategory-11.0.0.txt - * # Date: 2018-02-05, 16:21:00 GMT [KW, RP] - * # Blocks-11.0.0.txt - * # Date: 2017-10-16, 24:39:00 GMT [KW] + * # IndicSyllabicCategory-12.0.0.txt + * # Date: 2019-01-31, 02:26:00 GMT [KW, RP] + * # IndicPositionalCategory-12.0.0.txt + * # Date: 2019-01-31, 02:26:00 GMT [KW, RP] + * # Blocks-12.0.0.txt + * # Date: 2018-07-30, 19:40:00 GMT [KW] */ #include "hb-ot-shape-complex-indic.hh" @@ -19,21 +19,21 @@ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wunused-macros" -#define ISC_A INDIC_SYLLABIC_CATEGORY_AVAGRAHA /* 16 chars; Avagraha */ -#define ISC_Bi INDIC_SYLLABIC_CATEGORY_BINDU /* 83 chars; Bindu */ +#define ISC_A INDIC_SYLLABIC_CATEGORY_AVAGRAHA /* 17 chars; Avagraha */ +#define ISC_Bi INDIC_SYLLABIC_CATEGORY_BINDU /* 86 chars; Bindu */ #define ISC_BJN INDIC_SYLLABIC_CATEGORY_BRAHMI_JOINING_NUMBER /* 20 chars; Brahmi_Joining_Number */ -#define ISC_Ca INDIC_SYLLABIC_CATEGORY_CANTILLATION_MARK /* 58 chars; Cantillation_Mark */ -#define ISC_C INDIC_SYLLABIC_CATEGORY_CONSONANT /* 2110 chars; Consonant */ -#define ISC_CD INDIC_SYLLABIC_CATEGORY_CONSONANT_DEAD /* 10 chars; Consonant_Dead */ +#define ISC_Ca INDIC_SYLLABIC_CATEGORY_CANTILLATION_MARK /* 59 chars; Cantillation_Mark */ +#define ISC_C INDIC_SYLLABIC_CATEGORY_CONSONANT /* 2160 chars; Consonant */ +#define ISC_CD INDIC_SYLLABIC_CATEGORY_CONSONANT_DEAD /* 12 chars; Consonant_Dead */ #define ISC_CF INDIC_SYLLABIC_CATEGORY_CONSONANT_FINAL /* 67 chars; Consonant_Final */ #define ISC_CHL INDIC_SYLLABIC_CATEGORY_CONSONANT_HEAD_LETTER /* 5 chars; Consonant_Head_Letter */ #define ISC_CIP INDIC_SYLLABIC_CATEGORY_CONSONANT_INITIAL_POSTFIXED /* 1 chars; Consonant_Initial_Postfixed */ #define ISC_CK INDIC_SYLLABIC_CATEGORY_CONSONANT_KILLER /* 2 chars; Consonant_Killer */ -#define ISC_CM INDIC_SYLLABIC_CATEGORY_CONSONANT_MEDIAL /* 28 chars; Consonant_Medial */ -#define ISC_CP INDIC_SYLLABIC_CATEGORY_CONSONANT_PLACEHOLDER /* 21 chars; Consonant_Placeholder */ +#define ISC_CM INDIC_SYLLABIC_CATEGORY_CONSONANT_MEDIAL /* 29 chars; Consonant_Medial */ +#define ISC_CP INDIC_SYLLABIC_CATEGORY_CONSONANT_PLACEHOLDER /* 22 chars; Consonant_Placeholder */ #define ISC_CPR INDIC_SYLLABIC_CATEGORY_CONSONANT_PRECEDING_REPHA /* 2 chars; Consonant_Preceding_Repha */ -#define ISC_CPrf INDIC_SYLLABIC_CATEGORY_CONSONANT_PREFIXED /* 7 chars; Consonant_Prefixed */ -#define ISC_CS INDIC_SYLLABIC_CATEGORY_CONSONANT_SUBJOINED /* 95 chars; Consonant_Subjoined */ +#define ISC_CPrf INDIC_SYLLABIC_CATEGORY_CONSONANT_PREFIXED /* 9 chars; Consonant_Prefixed */ +#define ISC_CS INDIC_SYLLABIC_CATEGORY_CONSONANT_SUBJOINED /* 94 chars; Consonant_Subjoined */ #define ISC_CSR INDIC_SYLLABIC_CATEGORY_CONSONANT_SUCCEEDING_REPHA /* 4 chars; Consonant_Succeeding_Repha */ #define ISC_CWS INDIC_SYLLABIC_CATEGORY_CONSONANT_WITH_STACKER /* 6 chars; Consonant_With_Stacker */ #define ISC_GM INDIC_SYLLABIC_CATEGORY_GEMINATION_MARK /* 3 chars; Gemination_Mark */ @@ -42,7 +42,7 @@ #define ISC_ML INDIC_SYLLABIC_CATEGORY_MODIFYING_LETTER /* 1 chars; Modifying_Letter */ #define ISC_ZWNJ INDIC_SYLLABIC_CATEGORY_NON_JOINER /* 1 chars; Non_Joiner */ #define ISC_N INDIC_SYLLABIC_CATEGORY_NUKTA /* 30 chars; Nukta */ -#define ISC_Nd INDIC_SYLLABIC_CATEGORY_NUMBER /* 480 chars; Number */ +#define ISC_Nd INDIC_SYLLABIC_CATEGORY_NUMBER /* 481 chars; Number */ #define ISC_NJ INDIC_SYLLABIC_CATEGORY_NUMBER_JOINER /* 1 chars; Number_Joiner */ #define ISC_x INDIC_SYLLABIC_CATEGORY_OTHER /* 1 chars; Other */ #define ISC_PK INDIC_SYLLABIC_CATEGORY_PURE_KILLER /* 21 chars; Pure_Killer */ @@ -50,21 +50,21 @@ #define ISC_SM INDIC_SYLLABIC_CATEGORY_SYLLABLE_MODIFIER /* 25 chars; Syllable_Modifier */ #define ISC_TL INDIC_SYLLABIC_CATEGORY_TONE_LETTER /* 7 chars; Tone_Letter */ #define ISC_TM INDIC_SYLLABIC_CATEGORY_TONE_MARK /* 42 chars; Tone_Mark */ -#define ISC_V INDIC_SYLLABIC_CATEGORY_VIRAMA /* 25 chars; Virama */ -#define ISC_Vs INDIC_SYLLABIC_CATEGORY_VISARGA /* 36 chars; Visarga */ +#define ISC_V INDIC_SYLLABIC_CATEGORY_VIRAMA /* 27 chars; Virama */ +#define ISC_Vs INDIC_SYLLABIC_CATEGORY_VISARGA /* 35 chars; Visarga */ #define ISC_Vo INDIC_SYLLABIC_CATEGORY_VOWEL /* 30 chars; Vowel */ -#define ISC_M INDIC_SYLLABIC_CATEGORY_VOWEL_DEPENDENT /* 660 chars; Vowel_Dependent */ -#define ISC_VI INDIC_SYLLABIC_CATEGORY_VOWEL_INDEPENDENT /* 464 chars; Vowel_Independent */ +#define ISC_M INDIC_SYLLABIC_CATEGORY_VOWEL_DEPENDENT /* 673 chars; Vowel_Dependent */ +#define ISC_VI INDIC_SYLLABIC_CATEGORY_VOWEL_INDEPENDENT /* 476 chars; Vowel_Independent */ -#define IMC_B INDIC_MATRA_CATEGORY_BOTTOM /* 340 chars; Bottom */ +#define IMC_B INDIC_MATRA_CATEGORY_BOTTOM /* 349 chars; Bottom */ #define IMC_BL INDIC_MATRA_CATEGORY_BOTTOM_AND_LEFT /* 1 chars; Bottom_And_Left */ #define IMC_BR INDIC_MATRA_CATEGORY_BOTTOM_AND_RIGHT /* 2 chars; Bottom_And_Right */ -#define IMC_L INDIC_MATRA_CATEGORY_LEFT /* 59 chars; Left */ +#define IMC_L INDIC_MATRA_CATEGORY_LEFT /* 61 chars; Left */ #define IMC_LR INDIC_MATRA_CATEGORY_LEFT_AND_RIGHT /* 21 chars; Left_And_Right */ #define IMC_x INDIC_MATRA_CATEGORY_NOT_APPLICABLE /* 1 chars; Not_Applicable */ #define IMC_O INDIC_MATRA_CATEGORY_OVERSTRUCK /* 10 chars; Overstruck */ -#define IMC_R INDIC_MATRA_CATEGORY_RIGHT /* 276 chars; Right */ -#define IMC_T INDIC_MATRA_CATEGORY_TOP /* 393 chars; Top */ +#define IMC_R INDIC_MATRA_CATEGORY_RIGHT /* 281 chars; Right */ +#define IMC_T INDIC_MATRA_CATEGORY_TOP /* 398 chars; Top */ #define IMC_TB INDIC_MATRA_CATEGORY_TOP_AND_BOTTOM /* 10 chars; Top_And_Bottom */ #define IMC_TBR INDIC_MATRA_CATEGORY_TOP_AND_BOTTOM_AND_RIGHT /* 1 chars; Top_And_Bottom_And_Right */ #define IMC_TL INDIC_MATRA_CATEGORY_TOP_AND_LEFT /* 6 chars; Top_And_Left */ @@ -152,7 +152,7 @@ static const INDIC_TABLE_ELEMENT_TYPE indic_table[] = { /* 0A38 */ _(C,x), _(C,x), _(x,x), _(x,x), _(N,B), _(x,x), _(M,R), _(M,L), /* 0A40 */ _(M,R), _(M,B), _(M,B), _(x,x), _(x,x), _(x,x), _(x,x), _(M,T), /* 0A48 */ _(M,T), _(x,x), _(x,x), _(M,T), _(M,T), _(V,B), _(x,x), _(x,x), - /* 0A50 */ _(x,x), _(Ca,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), + /* 0A50 */ _(x,x), _(Ca,B), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), /* 0A58 */ _(x,x), _(C,x), _(C,x), _(C,x), _(C,x), _(x,x), _(C,x), _(x,x), /* 0A60 */ _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(Nd,x), _(Nd,x), /* 0A68 */ _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), _(Nd,x), @@ -237,7 +237,7 @@ static const INDIC_TABLE_ELEMENT_TYPE indic_table[] = { /* Kannada */ - /* 0C80 */ _(x,x), _(Bi,T), _(Bi,R), _(Vs,R), _(x,x), _(VI,x), _(VI,x), _(VI,x), + /* 0C80 */ _(Bi,x), _(Bi,T), _(Bi,R), _(Vs,R), _(x,x), _(VI,x), _(VI,x), _(VI,x), /* 0C88 */ _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(VI,x), _(x,x), _(VI,x), _(VI,x), /* 0C90 */ _(VI,x), _(x,x), _(VI,x), _(VI,x), _(VI,x), _(C,x), _(C,x), _(C,x), /* 0C98 */ _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), _(C,x), @@ -346,8 +346,8 @@ static const INDIC_TABLE_ELEMENT_TYPE indic_table[] = { /* 1CD8 */ _(Ca,B), _(Ca,B), _(Ca,T), _(Ca,T), _(Ca,B), _(Ca,B), _(Ca,B), _(Ca,B), /* 1CE0 */ _(Ca,T), _(Ca,R), _(x,O), _(x,O), _(x,O), _(x,O), _(x,O), _(x,O), /* 1CE8 */ _(x,O), _(x,x), _(x,x), _(x,x), _(x,x), _(x,B), _(x,x), _(x,x), - /* 1CF0 */ _(x,x), _(x,x), _(Vs,x), _(Vs,x), _(Ca,T),_(CWS,x),_(CWS,x), _(Ca,R), - /* 1CF8 */ _(Ca,x), _(Ca,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), + /* 1CF0 */ _(x,x), _(x,x), _(CD,x), _(CD,x), _(Ca,T),_(CWS,x),_(CWS,x), _(Ca,R), + /* 1CF8 */ _(Ca,x), _(Ca,x), _(CP,x), _(x,x), _(x,x), _(x,x), _(x,x), _(x,x), #define indic_offset_0x2008u 1656 diff --git a/src/hb-ot-shape-complex-use-table.cc b/src/hb-ot-shape-complex-use-table.cc index 835b95bbf..6c054a626 100644 --- a/src/hb-ot-shape-complex-use-table.cc +++ b/src/hb-ot-shape-complex-use-table.cc @@ -6,12 +6,12 @@ * * on files with these headers: * - * # IndicSyllabicCategory-11.0.0.txt - * # Date: 2018-05-21, 18:33:00 GMT [KW, RP] - * # IndicPositionalCategory-11.0.0.txt - * # Date: 2018-02-05, 16:21:00 GMT [KW, RP] - * # Blocks-11.0.0.txt - * # Date: 2017-10-16, 24:39:00 GMT [KW] + * # IndicSyllabicCategory-12.0.0.txt + * # Date: 2019-01-31, 02:26:00 GMT [KW, RP] + * # IndicPositionalCategory-12.0.0.txt + * # Date: 2019-01-31, 02:26:00 GMT [KW, RP] + * # Blocks-12.0.0.txt + * # Date: 2018-07-30, 19:40:00 GMT [KW] * UnicodeData.txt does not have a header. */ @@ -167,7 +167,7 @@ static const USE_TABLE_ELEMENT_TYPE use_table[] = { /* Kannada */ - /* 0C80 */ O, VMAbv, VMPst, VMPst, O, B, B, B, B, B, B, B, B, O, B, B, + /* 0C80 */ B, VMAbv, VMPst, VMPst, O, B, B, B, B, B, B, B, B, O, B, B, /* 0C90 */ B, O, B, B, B, B, B, B, B, B, B, B, B, B, B, B, /* 0CA0 */ B, B, B, B, B, B, B, B, B, O, B, B, B, B, B, B, /* 0CB0 */ B, B, B, B, O, B, B, B, B, B, O, O, CMBlw, B, VPst, VAbv, @@ -288,7 +288,7 @@ static const USE_TABLE_ELEMENT_TYPE use_table[] = { /* 19A0 */ B, B, B, B, B, B, B, B, B, B, B, B, O, O, O, O, /* 19B0 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, /* 19C0 */ B, B, B, B, B, B, B, B, VMPst, VMPst, O, O, O, O, O, O, - /* 19D0 */ B, B, B, B, B, B, B, B, B, B, O, O, O, O, O, O, + /* 19D0 */ B, B, B, B, B, B, B, B, B, B, B, O, O, O, O, O, /* 19E0 */ O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, /* 19F0 */ O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, @@ -351,7 +351,7 @@ static const USE_TABLE_ELEMENT_TYPE use_table[] = { /* 1CD0 */ VMAbv, VMAbv, VMAbv, O, VMBlw, VMBlw, VMBlw, VMBlw, VMBlw, VMBlw, VMAbv, VMAbv, VMBlw, VMBlw, VMBlw, VMBlw, /* 1CE0 */ VMAbv, VMPst, VMBlw, VMBlw, VMBlw, VMBlw, VMBlw, VMBlw, VMBlw, O, O, O, O, VMBlw, O, O, - /* 1CF0 */ O, O, VMPst, VMPst, VMAbv, CS, CS, VMPst, VMAbv, VMAbv, O, O, O, O, O, O, + /* 1CF0 */ O, O, IND, IND, VMAbv, CS, CS, VMPst, VMAbv, VMAbv, GB, O, O, O, O, O, #define use_offset_0x1df8u 2736 @@ -393,7 +393,7 @@ static const USE_TABLE_ELEMENT_TYPE use_table[] = { /* Syloti Nagri */ - /* A800 */ B, B, O, B, B, B, VAbv, B, B, B, B, VMAbv, B, B, B, B, + /* A800 */ B, B, VAbv, B, B, B, H, B, B, B, B, VMAbv, B, B, B, B, /* A810 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, /* A820 */ B, B, B, VPst, VPst, VBlw, VAbv, VPst, O, O, O, O, O, O, O, O, /* A830 */ O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, @@ -438,7 +438,7 @@ static const USE_TABLE_ELEMENT_TYPE use_table[] = { /* A980 */ VMAbv, VMAbv, FAbv, VMPst, B, B, B, B, B, B, B, B, B, B, B, B, /* A990 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, /* A9A0 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, - /* A9B0 */ B, B, B, CMAbv, VPst, VPst, VAbv, VAbv, VBlw, VBlw, VPre, VPre, VAbv, SUB, MPst, MBlw, + /* A9B0 */ B, B, B, CMAbv, VPst, VPst, VAbv, VAbv, VBlw, VBlw, VPre, VPre, VAbv, MBlw, MPst, MBlw, /* A9C0 */ H, O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, /* A9D0 */ B, B, B, B, B, B, B, B, B, B, O, O, O, O, O, O, @@ -600,7 +600,7 @@ static const USE_TABLE_ELEMENT_TYPE use_table[] = { /* 11420 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, /* 11430 */ B, B, B, B, B, VPst, VPre, VPst, VBlw, VBlw, VBlw, VBlw, VBlw, VBlw, VAbv, VAbv, /* 11440 */ VPst, VPst, H, VMAbv, VMAbv, VMPst, CMBlw, B, O, O, O, O, O, O, O, O, - /* 11450 */ B, B, B, B, B, B, B, B, B, B, O, O, O, O, FM, O, + /* 11450 */ B, B, B, B, B, B, B, B, B, B, O, O, O, O, FM, B, /* 11460 */ O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, /* 11470 */ O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, @@ -643,7 +643,7 @@ static const USE_TABLE_ELEMENT_TYPE use_table[] = { /* 11680 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, /* 11690 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, /* 116A0 */ B, B, B, B, B, B, B, B, B, B, B, VMAbv, VMPst, VAbv, VPre, VPst, - /* 116B0 */ VBlw, VBlw, VAbv, VAbv, VAbv, VAbv, H, CMBlw, O, O, O, O, O, O, O, O, + /* 116B0 */ VBlw, VBlw, VAbv, VAbv, VAbv, VAbv, H, CMBlw, B, O, O, O, O, O, O, O, /* 116C0 */ B, B, B, B, B, B, B, B, B, B, O, O, O, O, O, O, /* 116D0 */ O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, /* 116E0 */ O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, @@ -666,9 +666,18 @@ static const USE_TABLE_ELEMENT_TYPE use_table[] = { /* 11820 */ B, B, B, B, B, B, B, B, B, B, B, B, VPst, VPre, VPst, VBlw, /* 11830 */ VBlw, VBlw, VBlw, VAbv, VAbv, VAbv, VAbv, VMAbv, VMPst, H, CMBlw, O, O, O, O, O, -#define use_offset_0x11a00u 5232 +#define use_offset_0x119a0u 5232 + /* Nandinagari */ + + /* 119A0 */ B, B, B, B, B, B, B, B, O, O, B, B, B, B, B, B, + /* 119B0 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, + /* 119C0 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, + /* 119D0 */ B, VPst, VPre, VPst, VBlw, VBlw, VBlw, VBlw, O, O, VAbv, VAbv, VPst, VPst, VMPst, VMPst, + /* 119E0 */ H, B, O, O, VPre, O, O, O, O, O, O, O, O, O, O, O, + /* 119F0 */ O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, + /* Zanabazar Square */ /* 11A00 */ B, VAbv, VBlw, VBlw, VAbv, VAbv, VAbv, VAbv, VAbv, VAbv, VBlw, B, B, B, B, B, @@ -682,10 +691,10 @@ static const USE_TABLE_ELEMENT_TYPE use_table[] = { /* 11A50 */ B, VAbv, VBlw, VBlw, VAbv, VAbv, VAbv, VPst, VPst, VBlw, VBlw, VBlw, B, B, B, B, /* 11A60 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, /* 11A70 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, - /* 11A80 */ B, B, B, B, O, O, R, R, R, R, FBlw, FBlw, FBlw, FBlw, FBlw, FBlw, + /* 11A80 */ B, B, B, B, R, R, R, R, R, R, FBlw, FBlw, FBlw, FBlw, FBlw, FBlw, /* 11A90 */ FBlw, FBlw, FBlw, FBlw, FBlw, FBlw, VMAbv, VMPst, CMAbv, H, O, O, O, B, O, O, -#define use_offset_0x11c00u 5392 +#define use_offset_0x11c00u 5488 /* Bhaiksuki */ @@ -706,7 +715,7 @@ static const USE_TABLE_ELEMENT_TYPE use_table[] = { /* 11CA0 */ SUB, SUB, SUB, SUB, SUB, SUB, SUB, SUB, O, SUB, SUB, SUB, SUB, SUB, SUB, SUB, /* 11CB0 */ VBlw, VPre, VBlw, VAbv, VPst, VMAbv, VMAbv, O, -#define use_offset_0x11d00u 5576 +#define use_offset_0x11d00u 5672 /* Masaram Gondi */ @@ -726,7 +735,7 @@ static const USE_TABLE_ELEMENT_TYPE use_table[] = { /* 11D90 */ VAbv, VAbv, O, VPst, VPst, VMAbv, VMPst, H, O, O, O, O, O, O, O, O, /* 11DA0 */ B, B, B, B, B, B, B, B, B, B, O, O, O, O, O, O, -#define use_offset_0x11ee0u 5752 +#define use_offset_0x11ee0u 5848 /* Makasar */ @@ -734,7 +743,7 @@ static const USE_TABLE_ELEMENT_TYPE use_table[] = { /* 11EE0 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, /* 11EF0 */ B, B, GB, VAbv, VBlw, VPre, VPst, O, -}; /* Table items: 5776; occupancy: 74% */ +}; /* Table items: 5872; occupancy: 74% */ USE_TABLE_ELEMENT_TYPE hb_use_get_category (hb_codepoint_t u) @@ -785,7 +794,7 @@ hb_use_get_category (hb_codepoint_t u) if (hb_in_range (u, 0x11400u, 0x114DFu)) return use_table[u - 0x11400u + use_offset_0x11400u]; if (hb_in_range (u, 0x11580u, 0x1173Fu)) return use_table[u - 0x11580u + use_offset_0x11580u]; if (hb_in_range (u, 0x11800u, 0x1183Fu)) return use_table[u - 0x11800u + use_offset_0x11800u]; - if (hb_in_range (u, 0x11A00u, 0x11A9Fu)) return use_table[u - 0x11A00u + use_offset_0x11a00u]; + if (hb_in_range (u, 0x119A0u, 0x11A9Fu)) return use_table[u - 0x119A0u + use_offset_0x119a0u]; if (hb_in_range (u, 0x11C00u, 0x11CB7u)) return use_table[u - 0x11C00u + use_offset_0x11c00u]; if (hb_in_range (u, 0x11D00u, 0x11DAFu)) return use_table[u - 0x11D00u + use_offset_0x11d00u]; if (hb_in_range (u, 0x11EE0u, 0x11EF7u)) return use_table[u - 0x11EE0u + use_offset_0x11ee0u]; diff --git a/src/hb-ot-shape-complex-vowel-constraints.cc b/src/hb-ot-shape-complex-vowel-constraints.cc index 0e532581d..4652d02c1 100644 --- a/src/hb-ot-shape-complex-vowel-constraints.cc +++ b/src/hb-ot-shape-complex-vowel-constraints.cc @@ -9,8 +9,8 @@ * # Copied from https://docs.microsoft.com/en-us/typography/script-development/use * # On October 23, 2018; with documentd dated 02/07/2018. * - * # Scripts-11.0.0.txt - * # Date: 2018-02-21, 05:34:31 GMT + * # Scripts-12.0.0.txt + * # Date: 2019-01-28, 22:16:47 GMT */ #include "hb-ot-shape-complex-vowel-constraints.hh" diff --git a/src/hb-ot-shape-complex.hh b/src/hb-ot-shape-complex.hh index a2499de9c..269162213 100644 --- a/src/hb-ot-shape-complex.hh +++ b/src/hb-ot-shape-complex.hh @@ -377,6 +377,9 @@ hb_ot_shape_complex_categorize (const hb_ot_shape_planner_t *planner) case HB_SCRIPT_MAKASAR: //case HB_SCRIPT_SOGDIAN: + /* Unicode-12.0 additions */ + case HB_SCRIPT_NANDINAGARI: + /* If the designer designed the font for the 'DFLT' script, * (or we ended up arbitrarily pick 'latn'), use the default shaper. * Otherwise, use the specific shaper. diff --git a/src/hb-ot-tag-table.hh b/src/hb-ot-tag-table.hh index b7090a0a7..09e162718 100644 --- a/src/hb-ot-tag-table.hh +++ b/src/hb-ot-tag-table.hh @@ -6,8 +6,8 @@ * * on files with these headers: * - * - * File-Date: 2018-08-08 + * + * File-Date: 2019-02-20 */ #ifndef HB_OT_TAG_TABLE_HH @@ -131,7 +131,7 @@ static const LangTag ot_languages[] = { {"bjt", {HB_TAG('B','L','N',' ')}}, /* Balanta-Ganja -> Balante */ {"bla", {HB_TAG('B','K','F',' ')}}, /* Siksika -> Blackfoot */ {"ble", {HB_TAG('B','L','N',' ')}}, /* Balanta-Kentohe -> Balante */ - {"blk", {HB_TAG('B','L','K',' ')}}, /* Pa'o Karen */ + {"blk", {HB_TAG('B','L','K',' ')}}, /* Pa’o Karen */ {"bln", {HB_TAG('B','I','K',' ')}}, /* Southern Catanduanes Bikol -> Bikol */ {"bm", {HB_TAG('B','M','B',' ')}}, /* Bambara (Bamanankan) */ {"bmm", {HB_TAG('M','L','G',' ')}}, /* Northern Betsimisaraka Malagasy -> Malagasy */ @@ -318,7 +318,7 @@ static const LangTag ot_languages[] = { {"fj", {HB_TAG('F','J','I',' ')}}, /* Fijian */ {"flm", {HB_TAG('H','A','L',' '), /* Halam (Falam Chin) (retired code) */ HB_TAG('Q','I','N',' ')}}, /* Falam Chin (retired code) -> Chin */ - {"fmp", {HB_TAG('F','M','P',' ')}}, /* Fe'fe' */ + {"fmp", {HB_TAG('F','M','P',' ')}}, /* Fe’fe’ */ {"fo", {HB_TAG('F','O','S',' ')}}, /* Faroese */ {"fon", {HB_TAG('F','O','N',' ')}}, /* Fon */ {"fr", {HB_TAG('F','R','A',' ')}}, /* French */ @@ -653,7 +653,7 @@ static const LangTag ot_languages[] = { {"mwr", {HB_TAG('M','A','W',' ')}}, /* Marwari [macrolanguage] */ {"mww", {HB_TAG('M','W','W',' ')}}, /* Hmong Daw */ {"my", {HB_TAG('B','R','M',' ')}}, /* Burmese */ - {"mym", {HB_TAG('M','E','N',' ')}}, /* Me'en */ + {"mym", {HB_TAG('M','E','N',' ')}}, /* Me’en */ {"myn", {HB_TAG('M','Y','N',' ')}}, /* Mayan [family] */ {"myq", {HB_TAG('M','N','K',' ')}}, /* Forest Maninka (retired code) -> Maninka */ {"myv", {HB_TAG('E','R','Z',' ')}}, /* Erzya */ @@ -687,7 +687,7 @@ static const LangTag ot_languages[] = { {"nog", {HB_TAG('N','O','G',' ')}}, /* Nogai */ {"nov", {HB_TAG('N','O','V',' ')}}, /* Novial */ {"npi", {HB_TAG('N','E','P',' ')}}, /* Nepali */ - {"nqo", {HB_TAG('N','K','O',' ')}}, /* N'Ko */ + {"nqo", {HB_TAG('N','K','O',' ')}}, /* N’Ko */ {"nr", {HB_TAG('N','D','B',' ')}}, /* South Ndebele -> Ndebele */ {"nsk", {HB_TAG('N','A','S',' ')}}, /* Naskapi */ {"nso", {HB_TAG('N','S','O',' ')}}, /* Pedi -> Sotho, Northern */ diff --git a/src/hb-ucdn.cc b/src/hb-ucdn.cc index 534935f13..8230bf1e8 100644 --- a/src/hb-ucdn.cc +++ b/src/hb-ucdn.cc @@ -171,6 +171,10 @@ static const hb_script_t ucdn_script_translate[] = HB_SCRIPT_MEDEFAIDRIN, HB_SCRIPT_OLD_SOGDIAN, HB_SCRIPT_SOGDIAN, + HB_SCRIPT_ELYMAIC, + HB_SCRIPT_NANDINAGARI, + HB_SCRIPT_NYIAKENG_PUACHUE_HMONG, + HB_SCRIPT_WANCHO, }; static hb_unicode_combining_class_t diff --git a/src/hb-ucdn/ucdn.h b/src/hb-ucdn/ucdn.h index 05d46d2b1..1317d4fe4 100644 --- a/src/hb-ucdn/ucdn.h +++ b/src/hb-ucdn/ucdn.h @@ -206,6 +206,17 @@ typedef unsigned __int64 uint64_t; #define UCDN_SCRIPT_NUSHU 139 #define UCDN_SCRIPT_SOYOMBO 140 #define UCDN_SCRIPT_ZANABAZAR_SQUARE 141 +#define UCDN_SCRIPT_DOGRA 142 +#define UCDN_SCRIPT_GUNJALA_GONDI 143 +#define UCDN_SCRIPT_HANIFI_ROHINGYA 144 +#define UCDN_SCRIPT_MAKASAR 145 +#define UCDN_SCRIPT_MEDEFAIDRIN 146 +#define UCDN_SCRIPT_OLD_SOGDIAN 147 +#define UCDN_SCRIPT_SOGDIAN 148 +#define UCDN_SCRIPT_ELYMAIC 149 +#define UCDN_SCRIPT_NANDINAGARI 150 +#define UCDN_SCRIPT_NYIAKENG_PUACHUE_HMONG 151 +#define UCDN_SCRIPT_WANCHO 152 #define UCDN_LINEBREAK_CLASS_OP 0 #define UCDN_LINEBREAK_CLASS_CL 1 diff --git a/src/hb-ucdn/ucdn_db.h b/src/hb-ucdn/ucdn_db.h index 87872b7ae..8f1310f29 100644 --- a/src/hb-ucdn/ucdn_db.h +++ b/src/hb-ucdn/ucdn_db.h @@ -1,6 +1,6 @@ /* this file was generated by makeunicodedata.py 3.2 */ -#define UNIDATA_VERSION "11.0.0" +#define UNIDATA_VERSION "12.0.0" /* a list of unique database records */ static const UCDRecord ucd_records[] = { {2, 0, 18, 5, 102, 39}, @@ -201,7 +201,6 @@ static const UCDRecord ucd_records[] = { {7, 0, 0, 5, 9, 12}, {12, 7, 13, 5, 9, 21}, {12, 9, 13, 5, 9, 21}, - {12, 230, 13, 5, 9, 21}, {21, 0, 0, 5, 0, 17}, {13, 0, 0, 5, 9, 11}, {21, 0, 0, 5, 9, 12}, @@ -257,6 +256,7 @@ static const UCDRecord ucd_records[] = { {12, 84, 13, 5, 15, 21}, {12, 91, 13, 5, 15, 21}, {13, 0, 0, 5, 15, 11}, + {21, 0, 0, 5, 15, 18}, {15, 0, 18, 5, 15, 12}, {26, 0, 0, 5, 15, 12}, {7, 0, 0, 5, 16, 12}, @@ -294,6 +294,7 @@ static const UCDRecord ucd_records[] = { {7, 0, 0, 5, 20, 36}, {12, 0, 13, 5, 20, 36}, {12, 118, 13, 5, 20, 36}, + {12, 9, 13, 5, 20, 36}, {6, 0, 0, 5, 20, 36}, {12, 122, 13, 5, 20, 36}, {13, 0, 0, 5, 20, 11}, @@ -348,6 +349,7 @@ static const UCDRecord ucd_records[] = { {5, 0, 0, 5, 26, 12}, {17, 0, 18, 5, 27, 17}, {7, 0, 0, 5, 27, 12}, + {26, 0, 0, 5, 27, 12}, {21, 0, 0, 5, 27, 12}, {29, 0, 17, 5, 28, 17}, {7, 0, 0, 5, 28, 12}, @@ -632,6 +634,7 @@ static const UCDRecord ucd_records[] = { {12, 0, 13, 5, 70, 21}, {21, 0, 0, 5, 70, 17}, {13, 0, 0, 5, 70, 11}, + {12, 230, 13, 5, 9, 21}, {21, 0, 0, 5, 9, 18}, {13, 0, 0, 5, 71, 11}, {7, 0, 0, 5, 71, 12}, @@ -817,6 +820,7 @@ static const UCDRecord ucd_records[] = { {12, 230, 13, 5, 148, 21}, {15, 0, 4, 5, 148, 12}, {21, 0, 4, 5, 148, 12}, + {7, 0, 3, 5, 149, 12}, {10, 0, 0, 5, 93, 21}, {12, 0, 13, 5, 93, 21}, {7, 0, 0, 5, 93, 12}, @@ -936,6 +940,11 @@ static const UCDRecord ucd_records[] = { {13, 0, 0, 5, 125, 11}, {15, 0, 0, 5, 125, 12}, {7, 0, 0, 5, 125, 12}, + {7, 0, 0, 5, 150, 12}, + {10, 0, 0, 5, 150, 21}, + {12, 0, 13, 5, 150, 21}, + {12, 9, 13, 5, 150, 21}, + {21, 0, 0, 5, 150, 18}, {7, 0, 0, 5, 141, 12}, {12, 0, 13, 5, 141, 21}, {12, 0, 0, 5, 141, 21}, @@ -977,12 +986,17 @@ static const UCDRecord ucd_records[] = { {12, 0, 13, 5, 145, 21}, {10, 0, 0, 5, 145, 21}, {21, 0, 0, 5, 145, 12}, + {23, 0, 10, 5, 14, 10}, + {21, 0, 0, 5, 14, 17}, {7, 0, 0, 5, 62, 12}, {14, 0, 0, 5, 62, 12}, {21, 0, 0, 5, 62, 17}, {7, 0, 0, 5, 80, 12}, {7, 0, 0, 5, 80, 0}, {7, 0, 0, 5, 80, 1}, + {1, 0, 0, 5, 80, 4}, + {1, 0, 0, 5, 80, 0}, + {1, 0, 0, 5, 80, 1}, {7, 0, 0, 5, 127, 12}, {7, 0, 0, 5, 127, 0}, {7, 0, 0, 5, 127, 1}, @@ -1006,11 +1020,12 @@ static const UCDRecord ucd_records[] = { {21, 0, 0, 5, 146, 17}, {21, 0, 0, 5, 146, 12}, {7, 0, 0, 5, 98, 12}, - {10, 0, 0, 5, 98, 21}, {12, 0, 13, 5, 98, 21}, + {10, 0, 0, 5, 98, 21}, {6, 0, 0, 5, 98, 12}, {6, 0, 0, 2, 137, 5}, {6, 0, 0, 2, 139, 5}, + {6, 0, 0, 2, 0, 5}, {7, 0, 0, 2, 137, 14}, {7, 0, 0, 2, 139, 14}, {7, 0, 0, 5, 105, 12}, @@ -1028,6 +1043,15 @@ static const UCDRecord ucd_records[] = { {21, 0, 0, 5, 131, 17}, {21, 0, 0, 5, 131, 12}, {12, 230, 13, 5, 56, 21}, + {7, 0, 0, 5, 151, 12}, + {12, 230, 13, 5, 151, 21}, + {6, 0, 0, 5, 151, 12}, + {13, 0, 0, 5, 151, 11}, + {26, 0, 0, 5, 151, 12}, + {7, 0, 0, 5, 152, 12}, + {12, 230, 13, 5, 152, 21}, + {13, 0, 0, 5, 152, 11}, + {23, 0, 10, 5, 152, 9}, {7, 0, 3, 5, 113, 12}, {15, 0, 3, 5, 113, 12}, {12, 220, 13, 5, 113, 21}, @@ -1035,11 +1059,13 @@ static const UCDRecord ucd_records[] = { {5, 0, 3, 5, 132, 12}, {12, 230, 13, 5, 132, 21}, {12, 7, 13, 5, 132, 21}, + {6, 0, 3, 5, 132, 12}, {13, 0, 3, 5, 132, 11}, {21, 0, 3, 5, 132, 0}, {15, 0, 4, 5, 0, 12}, {26, 0, 4, 5, 0, 10}, {23, 0, 4, 5, 0, 10}, + {26, 0, 4, 5, 0, 12}, {2, 0, 18, 5, 102, 14}, {26, 0, 0, 2, 0, 29}, {26, 0, 0, 5, 0, 28}, @@ -2012,6 +2038,10 @@ static const Reindex nfc_last[] = { #define UCDN_SCRIPT_MEDEFAIDRIN 146 #define UCDN_SCRIPT_OLD_SOGDIAN 147 #define UCDN_SCRIPT_SOGDIAN 148 +#define UCDN_SCRIPT_ELYMAIC 149 +#define UCDN_SCRIPT_NANDINAGARI 150 +#define UCDN_SCRIPT_NYIAKENG_PUACHUE_HMONG 151 +#define UCDN_SCRIPT_WANCHO 152 #define UCDN_GENERAL_CATEGORY_CC 0 #define UCDN_GENERAL_CATEGORY_CF 1 @@ -2087,23 +2117,23 @@ static const unsigned char index0[] = { 73, 73, 73, 73, 73, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 52, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, - 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 106, 108, 109, 110, 106, - 111, 111, 111, 112, 113, 114, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 115, 115, 116, 117, 118, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 119, 120, 121, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 122, 122, 123, 124, 106, 106, 125, 126, 127, 127, 127, 127, - 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, - 127, 127, 127, 127, 127, 128, 127, 127, 129, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 130, 131, 132, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 133, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 134, 135, 136, 137, 138, 139, - 140, 141, 142, 142, 143, 106, 106, 106, 106, 106, 144, 106, 106, 106, - 106, 106, 106, 106, 145, 146, 106, 106, 147, 106, 148, 106, 149, 150, - 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 160, 160, 160, 161, 52, + 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, + 113, 113, 113, 114, 115, 116, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 117, 117, 118, 119, 120, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 121, 122, 123, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 124, 124, 125, 126, 108, 108, 127, 128, 129, 129, 129, 129, + 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, + 129, 129, 129, 129, 129, 130, 129, 129, 131, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 132, 133, 134, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 135, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 136, 137, 138, 139, 140, 141, + 142, 143, 144, 144, 145, 108, 108, 108, 108, 108, 146, 147, 148, 108, + 108, 108, 108, 108, 149, 150, 108, 108, 151, 152, 153, 108, 154, 155, + 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 165, 165, 165, 166, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, @@ -2113,231 +2143,231 @@ static const unsigned char index0[] = { 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, - 52, 52, 52, 162, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, - 52, 52, 163, 164, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, - 52, 52, 52, 52, 52, 52, 52, 165, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, + 52, 52, 52, 167, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, + 52, 52, 168, 169, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, + 52, 52, 52, 52, 52, 52, 52, 170, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, - 166, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 52, 52, - 168, 167, 167, 167, 167, 169, 167, 167, 167, 167, 167, 167, 167, 167, - 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, - 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, - 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, - 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, - 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, - 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, - 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, - 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, - 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, - 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, - 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, - 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, - 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, - 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, - 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, - 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, - 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, - 167, 167, 167, 167, 167, 167, 167, 167, 167, 169, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 170, 171, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, - 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, 74, 74, 74, + 171, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 52, 52, + 173, 172, 172, 172, 172, 174, 172, 172, 172, 172, 172, 172, 172, 172, + 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, + 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, + 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, + 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, + 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, + 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, + 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, + 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, + 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, + 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, + 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, + 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, + 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, + 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, + 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, + 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, + 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, + 172, 172, 172, 172, 172, 172, 172, 172, 172, 174, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 175, 176, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, + 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, @@ -2352,7 +2382,7 @@ static const unsigned char index0[] = { 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, - 172, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, + 177, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, @@ -2366,7 +2396,7 @@ static const unsigned char index0[] = { 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, - 74, 74, 74, 74, 172, + 74, 74, 74, 74, 177, }; static const unsigned short index1[] = { @@ -2396,388 +2426,400 @@ static const unsigned short index1[] = { 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 119, 242, 243, 244, 245, 246, 243, 247, 248, 249, 250, 251, 119, 252, 253, 254, 255, 256, 257, 258, 259, 259, 258, 259, 260, 261, 262, 263, 264, 265, 266, - 119, 267, 268, 269, 270, 271, 271, 270, 272, 273, 274, 275, 276, 277, - 278, 279, 280, 119, 281, 282, 283, 284, 284, 284, 284, 285, 286, 287, - 288, 289, 290, 291, 292, 293, 294, 295, 296, 297, 295, 295, 298, 299, - 296, 300, 301, 302, 303, 304, 305, 119, 306, 307, 307, 307, 307, 307, - 308, 309, 310, 311, 312, 313, 119, 119, 119, 119, 314, 315, 316, 317, - 318, 319, 320, 321, 322, 323, 324, 325, 119, 119, 119, 119, 326, 327, - 328, 329, 330, 331, 332, 333, 334, 335, 334, 334, 334, 336, 337, 338, - 339, 340, 341, 342, 341, 341, 341, 343, 344, 345, 346, 347, 119, 119, - 119, 119, 348, 348, 348, 348, 348, 349, 350, 351, 352, 353, 354, 355, - 356, 357, 358, 348, 359, 360, 352, 361, 362, 362, 362, 362, 363, 364, - 365, 365, 365, 365, 365, 366, 367, 367, 367, 367, 367, 367, 367, 367, - 367, 367, 367, 367, 368, 368, 368, 368, 368, 368, 368, 368, 368, 369, - 369, 369, 369, 369, 369, 369, 369, 369, 369, 369, 370, 370, 370, 370, - 370, 370, 370, 370, 370, 371, 372, 371, 370, 370, 370, 370, 370, 371, - 370, 370, 370, 370, 371, 372, 371, 370, 372, 370, 370, 370, 370, 370, - 370, 370, 371, 370, 370, 370, 370, 370, 370, 370, 370, 373, 374, 375, - 376, 377, 370, 370, 378, 379, 380, 380, 380, 380, 380, 380, 380, 380, - 380, 380, 381, 382, 383, 384, 384, 384, 384, 384, 384, 384, 384, 384, - 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, - 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, - 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, - 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, - 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 385, 384, 384, - 386, 387, 387, 388, 389, 389, 389, 389, 389, 389, 389, 389, 389, 390, - 391, 392, 393, 394, 395, 119, 396, 396, 397, 119, 398, 398, 399, 119, - 400, 401, 402, 119, 403, 403, 403, 403, 403, 403, 404, 405, 406, 407, - 408, 409, 410, 411, 412, 413, 414, 415, 416, 417, 418, 418, 418, 418, - 419, 418, 418, 418, 418, 418, 418, 420, 421, 418, 418, 418, 418, 422, - 384, 384, 384, 384, 384, 384, 384, 384, 423, 119, 424, 424, 424, 425, - 426, 427, 428, 429, 430, 431, 432, 432, 432, 433, 434, 119, 435, 435, - 435, 435, 435, 436, 435, 435, 435, 437, 438, 439, 440, 440, 440, 440, - 441, 441, 442, 443, 444, 444, 444, 444, 444, 444, 445, 446, 447, 448, - 449, 450, 451, 452, 451, 452, 453, 454, 455, 456, 119, 119, 119, 119, - 119, 119, 119, 119, 457, 458, 458, 458, 458, 458, 459, 460, 461, 462, - 463, 464, 465, 466, 467, 468, 469, 470, 470, 470, 471, 472, 473, 474, - 475, 475, 475, 475, 476, 477, 478, 479, 480, 480, 480, 480, 481, 482, - 483, 484, 485, 486, 487, 488, 489, 489, 489, 490, 100, 491, 362, 362, - 362, 362, 362, 492, 493, 119, 494, 495, 496, 497, 498, 499, 54, 54, 54, - 54, 500, 501, 56, 56, 56, 56, 56, 502, 503, 504, 54, 505, 54, 54, 54, - 506, 56, 56, 56, 507, 508, 509, 510, 511, 511, 511, 512, 513, 27, 27, 27, - 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 514, 515, 27, - 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 516, 517, 518, 519, 516, 517, - 516, 517, 518, 519, 516, 520, 516, 517, 516, 518, 516, 521, 516, 521, - 516, 521, 522, 523, 524, 525, 526, 527, 516, 528, 529, 530, 531, 532, - 533, 534, 535, 536, 537, 538, 539, 540, 541, 542, 543, 544, 545, 546, - 547, 548, 56, 549, 550, 551, 552, 553, 554, 554, 555, 556, 557, 558, 559, - 119, 560, 561, 562, 563, 564, 565, 566, 567, 568, 569, 570, 571, 572, - 573, 572, 574, 575, 576, 577, 578, 579, 580, 581, 582, 581, 583, 584, - 581, 585, 581, 586, 587, 588, 589, 590, 591, 592, 593, 594, 595, 587, - 596, 597, 587, 598, 599, 587, 587, 599, 587, 600, 601, 600, 587, 587, - 602, 587, 587, 587, 587, 587, 603, 587, 587, 581, 604, 605, 606, 607, - 608, 609, 610, 610, 610, 610, 610, 610, 610, 610, 611, 581, 581, 612, - 613, 587, 587, 614, 581, 581, 581, 581, 586, 607, 615, 616, 581, 581, - 581, 581, 581, 617, 119, 119, 119, 581, 618, 119, 119, 619, 619, 619, - 619, 619, 620, 620, 621, 622, 622, 622, 622, 622, 622, 622, 622, 622, - 623, 619, 624, 625, 625, 625, 625, 625, 625, 625, 625, 625, 626, 625, - 625, 625, 625, 627, 581, 625, 625, 628, 581, 629, 630, 631, 632, 633, - 634, 630, 581, 628, 635, 581, 636, 637, 638, 639, 640, 581, 581, 581, - 641, 642, 643, 644, 581, 645, 646, 581, 647, 581, 581, 648, 649, 650, - 651, 581, 652, 653, 654, 655, 656, 657, 658, 659, 660, 661, 662, 581, - 581, 581, 663, 581, 664, 581, 665, 666, 667, 668, 669, 670, 619, 671, - 671, 672, 581, 581, 581, 663, 673, 674, 587, 587, 587, 675, 676, 587, - 587, 677, 677, 677, 677, 677, 677, 677, 677, 677, 677, 677, 677, 677, - 677, 677, 677, 677, 677, 677, 677, 677, 677, 677, 677, 677, 677, 677, - 677, 677, 677, 677, 677, 587, 587, 587, 587, 587, 587, 587, 587, 587, - 587, 587, 587, 587, 587, 587, 587, 678, 679, 679, 680, 587, 587, 587, - 587, 587, 587, 587, 681, 587, 587, 587, 682, 587, 587, 587, 587, 587, - 587, 587, 587, 587, 587, 587, 587, 587, 587, 587, 587, 587, 587, 587, - 587, 587, 587, 587, 587, 587, 587, 587, 587, 587, 587, 587, 587, 581, - 581, 581, 683, 581, 581, 587, 587, 684, 685, 686, 630, 581, 581, 687, - 581, 581, 581, 688, 581, 581, 581, 581, 581, 581, 689, 581, 581, 581, - 581, 581, 617, 690, 690, 690, 690, 690, 691, 692, 692, 692, 692, 692, - 693, 694, 695, 696, 697, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, - 698, 699, 700, 701, 365, 365, 365, 365, 702, 703, 704, 704, 704, 704, - 704, 704, 704, 705, 706, 707, 370, 370, 372, 119, 372, 372, 372, 372, - 372, 372, 372, 372, 708, 708, 708, 708, 709, 710, 711, 712, 713, 714, - 715, 716, 717, 718, 119, 119, 119, 119, 119, 119, 719, 719, 719, 720, - 719, 719, 719, 719, 719, 719, 719, 719, 719, 719, 721, 119, 719, 719, - 719, 719, 719, 719, 719, 719, 719, 719, 719, 719, 719, 719, 719, 719, - 719, 719, 719, 719, 719, 719, 719, 719, 719, 719, 722, 119, 119, 119, - 723, 724, 725, 726, 727, 728, 729, 730, 731, 732, 733, 734, 735, 735, - 736, 735, 735, 735, 737, 738, 739, 740, 741, 742, 743, 743, 744, 743, - 743, 743, 745, 746, 747, 748, 749, 750, 750, 750, 750, 750, 751, 752, - 752, 752, 752, 752, 752, 752, 752, 752, 752, 753, 754, 755, 750, 750, - 750, 756, 723, 723, 723, 723, 724, 119, 757, 757, 758, 758, 758, 759, - 760, 761, 755, 755, 755, 762, 763, 764, 758, 758, 758, 765, 760, 761, - 755, 755, 755, 755, 766, 764, 755, 767, 768, 768, 768, 768, 768, 769, - 768, 768, 768, 768, 768, 768, 768, 768, 768, 768, 768, 755, 755, 755, - 770, 771, 755, 755, 755, 755, 755, 755, 755, 755, 755, 755, 755, 772, - 755, 755, 755, 770, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, - 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, - 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, - 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, - 773, 773, 774, 775, 581, 581, 581, 581, 581, 581, 581, 581, 773, 773, - 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, - 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, - 775, 775, 776, 776, 777, 776, 776, 776, 776, 776, 776, 776, 776, 776, - 776, 776, 776, 776, 776, 776, 776, 776, 776, 776, 776, 776, 776, 776, - 776, 776, 776, 776, 776, 776, 776, 776, 776, 776, 776, 776, 776, 776, - 776, 776, 776, 776, 776, 776, 776, 776, 776, 776, 776, 776, 776, 776, - 776, 776, 776, 776, 776, 776, 776, 776, 776, 776, 776, 776, 776, 776, - 776, 776, 776, 776, 776, 776, 776, 776, 776, 776, 776, 776, 776, 778, - 779, 779, 779, 779, 779, 779, 780, 119, 781, 781, 781, 781, 781, 782, - 783, 783, 783, 783, 783, 783, 783, 783, 783, 783, 783, 783, 783, 783, - 783, 783, 783, 783, 783, 783, 783, 783, 783, 783, 783, 783, 783, 783, - 783, 783, 783, 783, 783, 784, 783, 783, 785, 786, 119, 119, 101, 101, - 101, 101, 101, 787, 788, 789, 101, 101, 101, 790, 791, 791, 791, 791, - 791, 791, 791, 791, 792, 793, 794, 119, 64, 64, 795, 796, 797, 27, 798, - 27, 27, 27, 27, 27, 27, 27, 799, 800, 27, 801, 802, 27, 27, 803, 804, - 805, 119, 119, 119, 119, 119, 119, 806, 807, 808, 809, 810, 810, 811, - 812, 813, 814, 815, 815, 815, 815, 815, 815, 816, 119, 817, 818, 818, - 818, 818, 818, 819, 820, 821, 822, 823, 824, 825, 825, 826, 827, 828, - 829, 830, 830, 831, 832, 833, 833, 834, 835, 836, 837, 367, 367, 367, - 838, 839, 840, 840, 840, 840, 840, 841, 842, 843, 844, 845, 846, 847, - 348, 352, 848, 849, 849, 849, 849, 849, 850, 851, 119, 852, 853, 854, - 855, 348, 348, 856, 857, 858, 858, 858, 858, 858, 858, 859, 860, 861, - 119, 119, 862, 863, 864, 865, 119, 866, 866, 866, 119, 372, 372, 54, 54, - 54, 54, 54, 867, 868, 119, 869, 869, 869, 869, 869, 869, 869, 869, 869, - 869, 863, 863, 863, 863, 870, 871, 872, 873, 874, 875, 875, 876, 875, - 875, 875, 874, 875, 875, 876, 875, 875, 875, 874, 875, 875, 876, 875, - 875, 875, 874, 875, 875, 876, 875, 875, 875, 874, 875, 875, 876, 875, - 875, 875, 874, 875, 875, 876, 875, 875, 875, 874, 875, 875, 876, 875, - 875, 875, 874, 875, 875, 876, 875, 875, 875, 874, 875, 875, 876, 875, - 875, 875, 874, 875, 875, 876, 875, 875, 875, 874, 875, 875, 876, 875, - 875, 875, 874, 875, 875, 876, 875, 875, 875, 874, 875, 875, 876, 875, - 875, 875, 874, 875, 875, 876, 875, 875, 875, 874, 875, 875, 876, 875, - 875, 875, 874, 875, 875, 876, 875, 875, 875, 874, 875, 875, 876, 875, - 875, 875, 874, 875, 875, 876, 875, 875, 875, 874, 875, 875, 876, 875, - 875, 875, 874, 875, 875, 876, 875, 875, 875, 874, 875, 875, 876, 875, - 875, 875, 874, 875, 875, 876, 875, 875, 875, 874, 875, 875, 876, 875, - 875, 875, 874, 875, 875, 876, 875, 875, 875, 874, 875, 875, 876, 875, - 875, 875, 874, 875, 875, 876, 875, 875, 875, 874, 875, 875, 876, 875, - 875, 875, 874, 875, 875, 876, 875, 875, 875, 874, 875, 875, 876, 875, - 875, 875, 874, 875, 875, 876, 875, 875, 875, 874, 875, 875, 876, 875, - 875, 875, 874, 875, 875, 876, 875, 875, 875, 875, 875, 875, 874, 875, - 875, 876, 875, 875, 875, 874, 875, 875, 876, 875, 875, 875, 874, 875, - 875, 877, 119, 368, 368, 878, 879, 369, 369, 369, 369, 369, 880, 881, - 881, 881, 881, 881, 881, 881, 881, 881, 881, 881, 881, 881, 881, 881, - 881, 881, 881, 881, 881, 881, 881, 881, 881, 881, 881, 881, 881, 881, - 881, 881, 881, 882, 882, 882, 882, 882, 882, 882, 882, 882, 882, 882, - 882, 882, 882, 882, 882, 882, 882, 882, 882, 882, 882, 882, 882, 882, - 882, 882, 882, 882, 882, 882, 882, 773, 773, 773, 773, 773, 773, 773, - 773, 773, 773, 773, 773, 773, 774, 773, 773, 773, 773, 773, 773, 773, - 773, 773, 773, 773, 773, 773, 883, 775, 775, 775, 775, 884, 119, 885, - 886, 120, 887, 888, 889, 890, 120, 127, 127, 127, 127, 127, 127, 127, - 127, 127, 127, 127, 127, 891, 892, 893, 119, 894, 127, 127, 127, 127, + 267, 268, 269, 270, 271, 272, 272, 271, 273, 274, 275, 276, 277, 278, + 279, 280, 281, 119, 282, 283, 284, 285, 285, 285, 285, 286, 287, 288, + 289, 290, 291, 292, 293, 294, 295, 296, 297, 298, 296, 296, 299, 300, + 297, 301, 302, 303, 304, 305, 306, 119, 307, 308, 308, 308, 308, 308, + 309, 310, 311, 312, 313, 314, 119, 119, 119, 119, 315, 316, 317, 317, + 318, 317, 319, 320, 321, 322, 323, 324, 119, 119, 119, 119, 325, 326, + 327, 328, 329, 330, 331, 332, 333, 334, 333, 333, 333, 335, 336, 337, + 338, 339, 340, 341, 340, 340, 340, 342, 343, 344, 345, 346, 119, 119, + 119, 119, 347, 347, 347, 347, 347, 348, 349, 350, 351, 352, 353, 354, + 355, 356, 357, 347, 358, 359, 351, 360, 361, 361, 361, 361, 362, 363, + 364, 364, 364, 364, 364, 365, 366, 366, 366, 366, 366, 366, 366, 366, + 366, 366, 366, 366, 367, 367, 367, 367, 367, 367, 367, 367, 367, 368, + 368, 368, 368, 368, 368, 368, 368, 368, 368, 368, 369, 369, 369, 369, + 369, 369, 369, 369, 369, 370, 371, 370, 369, 369, 369, 369, 369, 370, + 369, 369, 369, 369, 370, 371, 370, 369, 371, 369, 369, 369, 369, 369, + 369, 369, 370, 369, 369, 369, 369, 369, 369, 369, 369, 372, 373, 374, + 375, 376, 369, 369, 377, 378, 379, 379, 379, 379, 379, 379, 379, 379, + 379, 379, 380, 381, 382, 383, 383, 383, 383, 383, 383, 383, 383, 383, + 383, 383, 383, 383, 383, 383, 383, 383, 383, 383, 383, 383, 383, 383, + 383, 383, 383, 383, 383, 383, 383, 383, 383, 383, 383, 383, 383, 383, + 383, 383, 383, 383, 383, 383, 383, 383, 383, 383, 383, 383, 383, 383, + 383, 383, 383, 383, 383, 383, 383, 383, 383, 383, 383, 383, 383, 383, + 383, 383, 383, 383, 383, 383, 383, 383, 383, 383, 383, 384, 383, 383, + 385, 386, 386, 387, 388, 388, 388, 388, 388, 388, 388, 388, 388, 389, + 390, 391, 392, 393, 394, 119, 395, 395, 396, 119, 397, 397, 398, 119, + 399, 400, 401, 119, 402, 402, 402, 402, 402, 402, 403, 404, 405, 406, + 407, 408, 409, 410, 411, 412, 413, 414, 415, 416, 417, 417, 417, 417, + 418, 417, 417, 417, 417, 417, 417, 419, 420, 417, 417, 417, 417, 421, + 383, 383, 383, 383, 383, 383, 383, 383, 422, 119, 423, 423, 423, 424, + 425, 426, 427, 428, 429, 430, 431, 431, 431, 432, 433, 119, 434, 434, + 434, 434, 434, 435, 434, 434, 434, 436, 437, 438, 439, 439, 439, 439, + 440, 440, 441, 442, 443, 443, 443, 443, 443, 443, 444, 445, 446, 447, + 448, 449, 450, 451, 450, 451, 452, 453, 454, 455, 119, 119, 119, 119, + 119, 119, 119, 119, 456, 457, 457, 457, 457, 457, 458, 459, 460, 461, + 462, 463, 464, 465, 466, 467, 468, 469, 469, 469, 470, 471, 472, 473, + 474, 474, 474, 474, 475, 476, 477, 478, 479, 479, 479, 479, 480, 481, + 482, 483, 484, 485, 486, 487, 488, 488, 488, 489, 100, 490, 361, 361, + 361, 361, 361, 491, 492, 119, 493, 494, 495, 496, 497, 498, 54, 54, 54, + 54, 499, 500, 56, 56, 56, 56, 56, 501, 502, 503, 54, 504, 54, 54, 54, + 505, 56, 56, 56, 506, 507, 508, 509, 510, 510, 510, 511, 512, 27, 27, 27, + 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 513, 514, 27, + 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 515, 516, 517, 518, 515, 516, + 515, 516, 517, 518, 515, 519, 515, 516, 515, 517, 515, 520, 515, 520, + 515, 520, 521, 522, 523, 524, 525, 526, 515, 527, 528, 529, 530, 531, + 532, 533, 534, 535, 536, 537, 538, 539, 540, 541, 542, 543, 544, 545, + 546, 547, 56, 548, 549, 550, 551, 552, 553, 553, 554, 555, 556, 557, 558, + 119, 559, 560, 561, 562, 563, 564, 565, 566, 567, 568, 569, 570, 571, + 572, 571, 573, 574, 575, 576, 577, 578, 579, 580, 581, 580, 582, 583, + 580, 584, 580, 585, 586, 587, 588, 589, 590, 591, 592, 593, 594, 586, + 595, 596, 586, 597, 598, 586, 586, 598, 586, 599, 600, 599, 586, 586, + 601, 586, 586, 586, 586, 586, 602, 586, 586, 580, 603, 604, 605, 606, + 607, 608, 609, 609, 609, 609, 609, 609, 609, 609, 610, 580, 580, 611, + 612, 586, 586, 613, 580, 580, 580, 580, 585, 606, 614, 615, 580, 580, + 580, 580, 580, 616, 119, 119, 119, 580, 617, 119, 119, 618, 618, 618, + 618, 618, 619, 619, 620, 621, 621, 621, 621, 621, 621, 621, 621, 621, + 622, 618, 623, 624, 624, 624, 624, 624, 624, 624, 624, 624, 625, 624, + 624, 624, 624, 626, 580, 624, 624, 627, 580, 628, 629, 630, 631, 632, + 633, 629, 580, 627, 634, 580, 635, 636, 637, 638, 639, 580, 580, 580, + 640, 641, 642, 643, 580, 644, 645, 580, 646, 580, 580, 647, 648, 649, + 650, 580, 651, 652, 653, 654, 655, 656, 657, 658, 659, 660, 661, 580, + 580, 580, 662, 580, 663, 580, 664, 665, 666, 667, 668, 669, 618, 670, + 670, 671, 580, 580, 580, 662, 672, 673, 586, 586, 586, 674, 675, 586, + 586, 676, 676, 676, 676, 676, 676, 676, 676, 676, 676, 676, 676, 676, + 676, 676, 676, 676, 676, 676, 676, 676, 676, 676, 676, 676, 676, 676, + 676, 676, 676, 676, 676, 586, 586, 586, 586, 586, 586, 586, 586, 586, + 586, 586, 586, 586, 586, 586, 586, 677, 678, 678, 679, 586, 586, 586, + 586, 586, 586, 586, 680, 586, 586, 586, 681, 586, 586, 586, 586, 586, + 586, 586, 586, 586, 586, 586, 586, 586, 586, 586, 586, 586, 586, 586, + 586, 586, 586, 586, 586, 586, 586, 586, 586, 586, 586, 586, 586, 580, + 580, 580, 682, 580, 580, 586, 586, 683, 684, 685, 629, 580, 580, 686, + 580, 580, 580, 687, 580, 580, 580, 580, 580, 580, 580, 580, 580, 580, + 580, 580, 580, 688, 688, 688, 688, 688, 689, 690, 690, 690, 690, 690, + 691, 692, 693, 694, 695, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, + 696, 697, 698, 699, 364, 364, 364, 364, 700, 701, 702, 702, 702, 702, + 702, 702, 702, 703, 704, 705, 369, 369, 371, 119, 371, 371, 371, 371, + 371, 371, 371, 371, 706, 706, 706, 706, 707, 708, 709, 710, 711, 712, + 713, 714, 715, 716, 119, 119, 119, 119, 119, 119, 717, 717, 717, 718, + 717, 717, 717, 717, 717, 717, 717, 717, 717, 717, 719, 119, 717, 717, + 717, 717, 717, 717, 717, 717, 717, 717, 717, 717, 717, 717, 717, 717, + 717, 717, 717, 717, 717, 717, 717, 717, 717, 717, 720, 119, 119, 119, + 721, 722, 723, 724, 725, 726, 727, 728, 729, 730, 731, 732, 733, 733, + 734, 733, 733, 733, 735, 736, 737, 738, 739, 740, 741, 741, 742, 741, + 741, 741, 743, 744, 745, 746, 747, 748, 748, 748, 748, 748, 749, 750, + 750, 750, 750, 750, 750, 750, 750, 750, 750, 751, 752, 753, 748, 748, + 748, 754, 721, 721, 721, 721, 722, 119, 755, 755, 756, 756, 756, 757, + 758, 759, 753, 753, 753, 760, 761, 762, 756, 756, 756, 763, 758, 759, + 753, 753, 753, 753, 764, 762, 753, 765, 766, 766, 766, 766, 766, 767, + 766, 766, 766, 766, 766, 766, 766, 766, 766, 766, 766, 753, 753, 753, + 768, 769, 753, 753, 753, 753, 753, 753, 753, 753, 753, 753, 753, 770, + 753, 753, 753, 768, 771, 771, 771, 771, 771, 771, 771, 771, 771, 771, + 771, 771, 771, 771, 771, 771, 771, 771, 771, 771, 771, 771, 771, 771, + 771, 771, 771, 771, 771, 771, 771, 771, 771, 771, 771, 771, 771, 771, + 771, 771, 771, 771, 771, 771, 771, 771, 771, 771, 771, 771, 771, 771, + 771, 771, 772, 773, 580, 580, 580, 580, 580, 580, 580, 580, 771, 771, + 771, 771, 771, 771, 771, 771, 771, 771, 771, 771, 771, 771, 771, 771, + 771, 771, 771, 771, 771, 771, 771, 771, 771, 771, 771, 771, 771, 771, + 773, 773, 774, 774, 775, 774, 774, 774, 774, 774, 774, 774, 774, 774, + 774, 774, 774, 774, 774, 774, 774, 774, 774, 774, 774, 774, 774, 774, + 774, 774, 774, 774, 774, 774, 774, 774, 774, 774, 774, 774, 774, 774, + 774, 774, 774, 774, 774, 774, 774, 774, 774, 774, 774, 774, 774, 774, + 774, 774, 774, 774, 774, 774, 774, 774, 774, 774, 774, 774, 774, 774, + 774, 774, 774, 774, 774, 774, 774, 774, 774, 774, 774, 774, 774, 776, + 777, 777, 777, 777, 777, 777, 778, 119, 779, 779, 779, 779, 779, 780, + 781, 781, 781, 781, 781, 781, 781, 781, 781, 781, 781, 781, 781, 781, + 781, 781, 781, 781, 781, 781, 781, 781, 781, 781, 781, 781, 781, 781, + 781, 781, 781, 781, 781, 782, 781, 781, 783, 784, 119, 119, 101, 101, + 101, 101, 101, 785, 786, 787, 101, 101, 101, 788, 789, 789, 789, 789, + 789, 789, 789, 789, 790, 791, 792, 119, 64, 64, 793, 794, 795, 27, 796, + 27, 27, 27, 27, 27, 27, 27, 797, 798, 27, 799, 800, 27, 27, 801, 802, 27, + 803, 119, 119, 119, 119, 119, 804, 805, 806, 807, 808, 808, 809, 810, + 811, 812, 813, 813, 813, 813, 813, 813, 814, 119, 815, 816, 816, 816, + 816, 816, 817, 818, 819, 820, 821, 822, 823, 823, 824, 825, 826, 827, + 828, 828, 829, 830, 831, 831, 832, 833, 834, 835, 366, 366, 366, 836, + 837, 838, 838, 838, 838, 838, 839, 840, 841, 842, 843, 844, 845, 347, + 351, 846, 847, 847, 847, 847, 847, 848, 849, 119, 850, 851, 852, 853, + 347, 347, 854, 855, 856, 856, 856, 856, 856, 856, 857, 858, 859, 119, + 119, 860, 861, 862, 863, 119, 864, 864, 864, 119, 371, 371, 54, 54, 54, + 54, 54, 865, 866, 119, 867, 867, 867, 867, 867, 867, 867, 867, 867, 867, + 861, 861, 861, 861, 868, 869, 870, 871, 872, 873, 873, 874, 873, 873, + 873, 872, 873, 873, 874, 873, 873, 873, 872, 873, 873, 874, 873, 873, + 873, 872, 873, 873, 874, 873, 873, 873, 872, 873, 873, 874, 873, 873, + 873, 872, 873, 873, 874, 873, 873, 873, 872, 873, 873, 874, 873, 873, + 873, 872, 873, 873, 874, 873, 873, 873, 872, 873, 873, 874, 873, 873, + 873, 872, 873, 873, 874, 873, 873, 873, 872, 873, 873, 874, 873, 873, + 873, 872, 873, 873, 874, 873, 873, 873, 872, 873, 873, 874, 873, 873, + 873, 872, 873, 873, 874, 873, 873, 873, 872, 873, 873, 874, 873, 873, + 873, 872, 873, 873, 874, 873, 873, 873, 872, 873, 873, 874, 873, 873, + 873, 872, 873, 873, 874, 873, 873, 873, 872, 873, 873, 874, 873, 873, + 873, 872, 873, 873, 874, 873, 873, 873, 872, 873, 873, 874, 873, 873, + 873, 872, 873, 873, 874, 873, 873, 873, 872, 873, 873, 874, 873, 873, + 873, 872, 873, 873, 874, 873, 873, 873, 872, 873, 873, 874, 873, 873, + 873, 872, 873, 873, 874, 873, 873, 873, 872, 873, 873, 874, 873, 873, + 873, 872, 873, 873, 874, 873, 873, 873, 872, 873, 873, 874, 873, 873, + 873, 872, 873, 873, 874, 873, 873, 873, 872, 873, 873, 874, 873, 873, + 873, 872, 873, 873, 874, 873, 873, 873, 873, 873, 873, 872, 873, 873, + 874, 873, 873, 873, 872, 873, 873, 874, 873, 873, 873, 872, 873, 873, + 875, 119, 367, 367, 876, 877, 368, 368, 368, 368, 368, 878, 879, 879, + 879, 879, 879, 879, 879, 879, 879, 879, 879, 879, 879, 879, 879, 879, + 879, 879, 879, 879, 879, 879, 879, 879, 879, 879, 879, 879, 879, 879, + 879, 879, 880, 880, 880, 880, 880, 880, 880, 880, 880, 880, 880, 880, + 880, 880, 880, 880, 880, 880, 880, 880, 880, 880, 880, 880, 880, 880, + 880, 880, 880, 880, 880, 880, 771, 771, 771, 771, 771, 771, 771, 771, + 771, 771, 771, 771, 771, 772, 771, 771, 771, 771, 771, 771, 771, 771, + 771, 771, 771, 771, 771, 881, 773, 773, 773, 773, 882, 119, 883, 884, + 120, 885, 886, 887, 888, 120, 127, 127, 127, 127, 127, 127, 127, 127, + 127, 127, 127, 127, 889, 890, 891, 119, 892, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, - 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 895, 119, - 119, 127, 127, 127, 127, 127, 127, 127, 127, 896, 127, 127, 127, 127, - 127, 127, 119, 119, 119, 119, 119, 127, 897, 898, 898, 899, 900, 901, - 902, 903, 904, 905, 906, 907, 908, 909, 910, 169, 127, 127, 127, 127, - 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 911, 912, - 913, 914, 915, 916, 917, 917, 918, 919, 920, 920, 921, 922, 923, 924, - 925, 925, 925, 925, 926, 927, 927, 927, 928, 929, 929, 929, 930, 931, - 932, 119, 933, 934, 935, 934, 934, 936, 934, 934, 937, 934, 938, 934, - 938, 119, 119, 119, 119, 934, 934, 934, 934, 934, 934, 934, 934, 934, - 934, 934, 934, 934, 934, 934, 939, 940, 941, 941, 941, 941, 941, 942, - 610, 943, 943, 943, 943, 943, 943, 944, 945, 946, 947, 581, 948, 949, - 119, 119, 119, 119, 119, 610, 610, 610, 610, 610, 950, 119, 119, 119, - 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 951, - 951, 951, 952, 953, 953, 953, 953, 953, 953, 954, 119, 955, 956, 956, - 957, 958, 958, 958, 958, 959, 960, 961, 961, 962, 963, 964, 964, 964, - 964, 965, 966, 967, 967, 967, 968, 969, 969, 969, 969, 970, 969, 971, - 119, 119, 119, 119, 119, 972, 972, 972, 972, 972, 973, 973, 973, 973, - 973, 974, 974, 974, 974, 974, 974, 975, 975, 975, 976, 977, 978, 979, - 979, 979, 979, 980, 981, 981, 981, 981, 982, 983, 983, 983, 983, 983, - 119, 984, 984, 984, 984, 984, 984, 985, 986, 119, 119, 119, 119, 119, - 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 987, - 987, 987, 987, 987, 987, 987, 987, 987, 987, 987, 987, 987, 987, 987, - 987, 987, 987, 987, 987, 987, 987, 987, 987, 987, 987, 987, 987, 987, - 987, 987, 987, 987, 987, 987, 987, 987, 987, 988, 119, 987, 987, 989, - 119, 987, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, - 119, 119, 119, 119, 119, 119, 119, 990, 991, 992, 992, 992, 992, 993, - 994, 995, 995, 996, 997, 998, 998, 999, 1000, 1001, 1001, 1001, 1002, - 1003, 1004, 119, 119, 119, 119, 119, 119, 1005, 1005, 1006, 1007, 1008, - 1008, 1009, 1010, 1011, 1011, 1011, 1012, 119, 119, 119, 119, 119, 119, - 119, 119, 1013, 1013, 1013, 1013, 1014, 1014, 1014, 1015, 1016, 1016, - 1017, 1016, 1016, 1016, 1016, 1016, 1018, 1019, 1020, 1021, 1022, 1022, - 1023, 1024, 1025, 1026, 1027, 1028, 1029, 1029, 1029, 1030, 1031, 1031, - 1031, 1032, 119, 119, 119, 119, 1033, 1034, 1033, 1033, 1035, 1036, 1037, - 119, 1038, 1038, 1038, 1038, 1038, 1038, 1039, 1040, 1041, 1041, 1042, - 1043, 1044, 1044, 1045, 1046, 1047, 1047, 1048, 1049, 119, 1050, 119, - 119, 119, 119, 119, 119, 119, 119, 119, 119, 1051, 1051, 1051, 1051, - 1051, 1051, 1051, 1051, 1051, 1052, 119, 119, 119, 119, 119, 119, 1053, - 1053, 1053, 1053, 1053, 1053, 1054, 119, 1055, 1055, 1055, 1055, 1055, - 1055, 1056, 1057, 1058, 1058, 1058, 1058, 1059, 119, 1060, 1061, 119, + 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 893, 119, 119, + 127, 127, 127, 127, 127, 127, 127, 127, 894, 127, 127, 127, 127, 127, + 127, 119, 119, 119, 119, 119, 127, 895, 896, 896, 897, 898, 899, 900, + 901, 902, 903, 904, 905, 906, 907, 908, 169, 127, 127, 127, 127, 127, + 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 909, 910, 911, + 912, 913, 914, 915, 915, 916, 917, 918, 918, 919, 920, 921, 922, 923, + 923, 923, 923, 924, 925, 925, 925, 926, 927, 927, 927, 928, 929, 930, + 119, 931, 932, 933, 932, 932, 934, 932, 932, 935, 932, 936, 932, 936, + 119, 119, 119, 119, 932, 932, 932, 932, 932, 932, 932, 932, 932, 932, + 932, 932, 932, 932, 932, 937, 938, 939, 939, 939, 939, 939, 940, 609, + 941, 941, 941, 941, 941, 941, 942, 943, 944, 945, 580, 946, 947, 119, + 119, 119, 119, 119, 609, 609, 609, 609, 609, 948, 119, 119, 119, 119, + 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 949, 949, + 949, 950, 951, 951, 951, 951, 951, 951, 952, 119, 953, 954, 954, 955, + 956, 956, 956, 956, 957, 958, 959, 959, 960, 961, 962, 962, 962, 962, + 963, 964, 965, 965, 965, 966, 967, 967, 967, 967, 968, 967, 969, 119, + 119, 119, 119, 119, 970, 970, 970, 970, 970, 971, 971, 971, 971, 971, + 972, 972, 972, 972, 972, 972, 973, 973, 973, 974, 975, 976, 977, 977, + 977, 977, 978, 979, 979, 979, 979, 980, 981, 981, 981, 981, 981, 119, + 982, 982, 982, 982, 982, 982, 983, 984, 119, 119, 119, 119, 119, 119, + 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 985, 985, + 985, 985, 985, 985, 985, 985, 985, 985, 985, 985, 985, 985, 985, 985, + 985, 985, 985, 985, 985, 985, 985, 985, 985, 985, 985, 985, 985, 985, + 985, 985, 985, 985, 985, 985, 985, 985, 986, 119, 985, 985, 987, 119, + 985, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, + 119, 119, 119, 119, 119, 119, 988, 989, 990, 990, 990, 990, 991, 992, + 993, 993, 994, 995, 996, 996, 997, 998, 999, 999, 999, 1000, 1001, 1002, + 119, 119, 119, 119, 119, 119, 1003, 1003, 1004, 1005, 1006, 1006, 1007, + 1008, 1009, 1009, 1009, 1010, 119, 119, 119, 119, 119, 119, 119, 119, + 1011, 1011, 1011, 1011, 1012, 1012, 1012, 1013, 1014, 1014, 1015, 1014, + 1014, 1014, 1014, 1014, 1016, 1017, 1018, 1019, 1020, 1020, 1021, 1022, + 1023, 1024, 1025, 1026, 1027, 1027, 1027, 1028, 1029, 1029, 1029, 1030, + 119, 119, 119, 119, 1031, 1032, 1031, 1031, 1033, 1034, 1035, 119, 1036, + 1036, 1036, 1036, 1036, 1036, 1037, 1038, 1039, 1039, 1040, 1041, 1042, + 1042, 1043, 1044, 1045, 1045, 1046, 1047, 119, 1048, 119, 119, 119, 119, + 119, 119, 119, 119, 119, 119, 1049, 1049, 1049, 1049, 1049, 1049, 1049, + 1049, 1049, 1050, 119, 119, 119, 119, 119, 119, 1051, 1051, 1051, 1051, + 1051, 1051, 1052, 119, 1053, 1053, 1053, 1053, 1053, 1053, 1054, 1055, + 1056, 1056, 1056, 1056, 1057, 119, 1058, 1059, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, - 119, 119, 119, 119, 119, 119, 119, 1062, 1062, 1062, 1063, 119, 119, 119, - 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 1064, - 1064, 1064, 1065, 1066, 119, 1067, 1067, 1068, 1069, 1070, 1071, 119, + 119, 119, 119, 1060, 1060, 1060, 1061, 119, 119, 119, 119, 119, 119, 119, + 119, 119, 119, 119, 119, 119, 119, 119, 119, 1062, 1062, 1062, 1063, + 1064, 119, 1065, 1065, 1066, 1067, 1068, 1069, 119, 119, 119, 119, 119, + 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 1070, 1070, 1071, + 119, 1072, 1073, 1073, 1073, 1073, 1073, 1073, 1074, 1075, 1076, 1077, + 1078, 1079, 1080, 119, 1081, 1082, 1083, 1083, 1083, 1083, 1083, 1084, + 1085, 1086, 1087, 1088, 1088, 1088, 1089, 1090, 1091, 1092, 1093, 1093, + 1093, 1094, 1095, 1096, 1097, 1098, 119, 1099, 1099, 1099, 1099, 1100, + 119, 1101, 1102, 1102, 1102, 1102, 1102, 1103, 1104, 1105, 1106, 1107, + 1108, 1109, 1110, 1111, 119, 1112, 1112, 1113, 1112, 1112, 1114, 1115, + 1116, 119, 119, 119, 119, 119, 119, 119, 119, 1117, 1118, 1119, 1120, + 1119, 1121, 1122, 1122, 1122, 1122, 1122, 1123, 1124, 1125, 1126, 1127, + 1128, 1129, 1130, 1131, 1131, 1132, 1133, 1134, 1135, 1136, 1137, 1138, + 1139, 1140, 1140, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, + 119, 119, 119, 119, 119, 119, 1141, 1141, 1141, 1141, 1141, 1141, 1142, + 1143, 1144, 1145, 1146, 1147, 119, 119, 119, 119, 1148, 1148, 1148, 1148, + 1148, 1148, 1149, 1150, 1151, 119, 1152, 1153, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, - 119, 119, 119, 119, 119, 1072, 1073, 1073, 1073, 1073, 1073, 1073, 1074, - 1075, 1076, 1077, 1078, 1079, 1080, 119, 1081, 1082, 1083, 1083, 1083, - 1083, 1083, 1084, 1085, 1086, 1087, 1088, 1088, 1088, 1089, 1090, 1091, - 1092, 1093, 1093, 1093, 1094, 1095, 1096, 1097, 1098, 119, 1099, 1099, - 1099, 1099, 1100, 119, 1101, 1102, 1102, 1102, 1102, 1102, 1103, 1104, - 1105, 1106, 1107, 1108, 1109, 1110, 1111, 119, 1112, 1112, 1113, 1112, - 1112, 1114, 1115, 1116, 119, 119, 119, 119, 119, 119, 119, 119, 1117, - 1118, 1119, 1120, 1119, 1121, 1122, 1122, 1122, 1122, 1122, 1123, 1124, - 1125, 1126, 1127, 1128, 1129, 1130, 1131, 1131, 1132, 1133, 1134, 1135, - 1136, 1137, 1138, 1139, 1140, 1140, 119, 119, 119, 119, 119, 119, 119, - 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 1141, 1141, 1141, 1141, - 1141, 1141, 1142, 1143, 1144, 1145, 1146, 1147, 119, 119, 119, 119, 1148, - 1148, 1148, 1148, 1148, 1148, 1149, 1150, 1151, 119, 1152, 1153, 119, + 119, 1154, 1154, 1154, 1154, 1154, 1155, 1156, 1157, 1158, 1159, 1160, + 1161, 119, 119, 119, 119, 1162, 1162, 1162, 1162, 1162, 1162, 1163, 1164, + 1165, 119, 1166, 1167, 1168, 1169, 119, 119, 1170, 1170, 1170, 1170, + 1170, 1171, 1172, 1173, 1174, 1175, 119, 119, 119, 119, 119, 119, 1176, + 1176, 1176, 1177, 1178, 1179, 1180, 1181, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, - 119, 119, 119, 119, 119, 1154, 1154, 1154, 1154, 1154, 1155, 1156, 1157, - 1158, 1159, 1160, 1161, 119, 119, 119, 119, 1162, 1162, 1162, 1162, 1162, - 1162, 1163, 1164, 1165, 119, 1166, 1167, 1168, 1169, 119, 119, 1170, - 1170, 1170, 1170, 1170, 1171, 1172, 119, 1173, 1174, 119, 119, 119, 119, - 119, 119, 1175, 1175, 1175, 1176, 1177, 1178, 1179, 1180, 119, 119, 119, + 119, 119, 119, 119, 1182, 1182, 1182, 1182, 1182, 1183, 1184, 1185, 119, + 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 1186, 1186, 1186, + 1186, 1187, 1187, 1187, 1187, 1188, 1189, 1190, 1191, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, - 119, 119, 119, 119, 119, 119, 119, 1181, 1181, 1181, 1181, 1181, 1182, - 1183, 1184, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, - 1185, 1185, 1185, 1185, 1186, 1186, 1186, 1186, 1187, 1188, 1189, 1190, + 119, 119, 1192, 1193, 1192, 1192, 1192, 1192, 1194, 1195, 1196, 119, 119, + 119, 1197, 1198, 1199, 1199, 1199, 1199, 1200, 1201, 1202, 119, 1203, + 1204, 1205, 1205, 1205, 1205, 1205, 1206, 1207, 1208, 1209, 119, 119, + 119, 1210, 1210, 1210, 1210, 1210, 1210, 1210, 1211, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, - 119, 119, 119, 119, 1191, 1192, 1193, 1193, 1193, 1193, 1194, 1195, 1196, - 119, 1197, 1198, 1199, 1199, 1199, 1199, 1200, 1201, 1202, 1203, 1204, - 119, 119, 119, 1205, 1205, 1205, 1205, 1205, 1205, 1205, 1206, 1207, - 1208, 1207, 1207, 1207, 1209, 1210, 1211, 1212, 119, 1213, 1214, 1215, - 1216, 1217, 1218, 1218, 1218, 1219, 1220, 1220, 1221, 1222, 119, 119, - 119, 119, 119, 119, 119, 119, 119, 1223, 1224, 1225, 1225, 1225, 1225, - 1226, 1227, 1228, 119, 1229, 1230, 1231, 1232, 1233, 1233, 1233, 1234, - 1235, 1236, 1237, 1238, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, + 1212, 1213, 1212, 1212, 1212, 1214, 1215, 1216, 1217, 119, 1218, 1219, + 1220, 1221, 1222, 1223, 1223, 1223, 1224, 1225, 1225, 1226, 1227, 119, + 119, 119, 119, 119, 119, 119, 119, 119, 1228, 1229, 1230, 1230, 1230, + 1230, 1231, 1232, 1233, 119, 1234, 1235, 1236, 1237, 1238, 1238, 1238, + 1239, 1240, 1241, 1242, 1243, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, - 1239, 1239, 1240, 1241, 1242, 1242, 1242, 1242, 1242, 1242, 1242, 1242, - 1242, 1242, 1242, 1242, 1242, 1242, 1242, 1242, 1242, 1242, 1242, 1242, - 1242, 1242, 1242, 1242, 1242, 1242, 1242, 1242, 1242, 1242, 1242, 1242, - 1242, 1242, 1242, 1242, 1242, 1242, 1242, 1242, 1242, 1242, 1242, 1242, - 1242, 1242, 1242, 1242, 1242, 1242, 1242, 1243, 119, 119, 119, 119, 119, - 119, 119, 119, 119, 119, 119, 119, 1244, 1244, 1244, 1244, 1244, 1244, - 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1245, 1246, 119, 1242, 1242, - 1242, 1242, 1242, 1242, 1242, 1242, 1242, 1242, 1242, 1242, 1242, 1242, - 1242, 1242, 1242, 1242, 1242, 1242, 1242, 1242, 1242, 1242, 1247, 119, + 119, 119, 1244, 1244, 1245, 1246, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, - 119, 119, 119, 119, 119, 119, 119, 119, 1248, 1248, 1248, 1248, 1248, - 1248, 1248, 1248, 1248, 1248, 1248, 1248, 1248, 1248, 1248, 1248, 1248, - 1248, 1248, 1248, 1248, 1248, 1248, 1248, 1248, 1248, 1248, 1248, 1248, - 1248, 1248, 1248, 1248, 1248, 1248, 1248, 1248, 1248, 1248, 1248, 1248, - 1248, 1248, 1249, 1248, 1248, 1248, 1248, 1250, 1251, 1248, 1248, 1248, - 1248, 1248, 1248, 1248, 1248, 1248, 1248, 1248, 1248, 1248, 1248, 1248, - 1248, 1248, 1248, 1248, 1248, 1248, 1248, 1248, 1248, 1248, 1248, 1248, - 1248, 1248, 1252, 1248, 1248, 1248, 1248, 1248, 1248, 1248, 1248, 1248, - 1248, 1248, 1248, 1248, 1248, 1248, 1248, 1248, 1248, 1248, 1248, 1248, - 1253, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, - 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 1254, + 119, 119, 1247, 1247, 1248, 1249, 1250, 1251, 1252, 1253, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, - 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1255, 1254, 1254, 1254, - 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1256, + 1254, 1255, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, + 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, + 1256, 1257, 1258, 119, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, + 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, + 1254, 1254, 1254, 1254, 1259, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, - 119, 119, 119, 119, 119, 119, 119, 119, 119, 791, 791, 791, 791, 791, - 791, 791, 791, 791, 791, 791, 791, 791, 791, 791, 791, 791, 791, 791, - 791, 791, 791, 791, 791, 791, 791, 791, 791, 791, 791, 791, 791, 791, - 791, 791, 791, 791, 791, 791, 1257, 1258, 1258, 1258, 1259, 1260, 1261, - 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 1262, 1262, - 1262, 1263, 1264, 119, 1265, 1265, 1265, 1265, 1265, 1265, 1266, 1267, - 1268, 119, 1269, 1270, 1271, 1265, 1265, 1272, 1265, 1265, 119, 119, 119, + 119, 1260, 1260, 1260, 1260, 1260, 1260, 1260, 1260, 1260, 1260, 1260, + 1260, 1260, 1260, 1260, 1260, 1260, 1260, 1260, 1260, 1260, 1260, 1260, + 1260, 1260, 1260, 1260, 1260, 1260, 1260, 1260, 1260, 1260, 1260, 1260, + 1260, 1260, 1260, 1260, 1260, 1260, 1260, 1260, 1261, 1260, 1260, 1260, + 1260, 1262, 1263, 1260, 1260, 1260, 1260, 1260, 1260, 1260, 1260, 1260, + 1260, 1260, 1260, 1260, 1260, 1260, 1260, 1260, 1260, 1260, 1260, 1260, + 1260, 1260, 1260, 1260, 1260, 1260, 1260, 1260, 1264, 1260, 1260, 1260, + 1260, 1260, 1260, 1260, 1260, 1260, 1260, 1260, 1260, 1260, 1260, 1260, + 1260, 1260, 1260, 1260, 1260, 1260, 1265, 1266, 1267, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, - 119, 119, 119, 119, 119, 1273, 1273, 1273, 1273, 1274, 1274, 1274, 1274, - 1275, 1275, 1276, 1277, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, - 119, 119, 1278, 1278, 1278, 1278, 1278, 1278, 1278, 1278, 1279, 119, - 1280, 1281, 1281, 1281, 1281, 1282, 119, 1283, 1284, 1285, 119, 119, 119, - 119, 119, 119, 119, 119, 1286, 119, 119, 119, 1287, 1287, 1287, 1287, - 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, - 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, - 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, - 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, - 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1288, 119, - 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, - 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, - 1287, 1287, 1287, 1287, 1287, 1287, 1289, 119, 1290, 735, 735, 735, 735, - 735, 735, 735, 735, 735, 735, 735, 735, 735, 735, 735, 735, 735, 735, - 735, 735, 735, 735, 735, 735, 735, 735, 735, 735, 735, 735, 735, 735, - 735, 735, 1291, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 1292, - 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, - 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, - 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, - 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, - 1293, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, - 1294, 1294, 1295, 1294, 1296, 1294, 1297, 1294, 1298, 1299, 119, 119, - 119, 119, 119, 119, 119, 119, 119, 119, 119, 610, 610, 610, 610, 610, - 610, 610, 610, 610, 610, 610, 610, 610, 610, 610, 610, 610, 610, 610, - 610, 610, 610, 610, 610, 610, 610, 610, 610, 610, 610, 1300, 119, 610, - 610, 610, 610, 1301, 1302, 610, 610, 610, 610, 610, 610, 1303, 1304, - 1305, 1306, 1307, 1308, 610, 610, 610, 1309, 610, 610, 610, 610, 610, - 610, 610, 1310, 119, 119, 946, 946, 946, 946, 946, 946, 946, 946, 1311, + 119, 119, 119, 119, 119, 119, 1268, 1268, 1268, 1268, 1268, 1268, 1268, + 1268, 1268, 1268, 1268, 1268, 1268, 1268, 1268, 1268, 1268, 1268, 1268, + 1268, 1268, 1268, 1268, 1268, 1268, 1268, 1268, 1268, 1268, 1268, 1268, + 1268, 1268, 1268, 1268, 1268, 1268, 1268, 1268, 1268, 1268, 1268, 1268, + 1268, 1268, 1268, 1268, 1268, 1268, 1268, 1268, 1268, 1268, 1268, 1268, + 1268, 1268, 1269, 1268, 1268, 1268, 1268, 1268, 1268, 1268, 1268, 1268, + 1268, 1268, 1268, 1268, 1268, 1270, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, - 119, 119, 119, 119, 119, 941, 941, 1312, 119, 581, 581, 581, 581, 581, - 581, 581, 581, 581, 581, 617, 119, 941, 941, 941, 1313, 119, 119, 119, - 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 1314, - 1314, 1314, 1315, 1316, 1316, 1317, 1314, 1314, 1318, 1319, 1316, 1316, - 1314, 1314, 1314, 1315, 1316, 1316, 1320, 1321, 1322, 1318, 1323, 1324, - 1316, 1314, 1314, 1314, 1315, 1316, 1316, 1325, 1326, 1327, 1328, 1316, - 1316, 1316, 1329, 1330, 1331, 1332, 1316, 1316, 1317, 1314, 1314, 1318, - 1316, 1316, 1316, 1314, 1314, 1314, 1315, 1316, 1316, 1317, 1314, 1314, - 1318, 1316, 1316, 1316, 1314, 1314, 1314, 1315, 1316, 1316, 1317, 1314, - 1314, 1318, 1316, 1316, 1316, 1314, 1314, 1314, 1315, 1316, 1316, 1333, - 1314, 1314, 1314, 1334, 1316, 1316, 1335, 1336, 1314, 1314, 1337, 1316, - 1316, 1338, 1317, 1314, 1314, 1339, 1316, 1316, 1340, 1341, 1314, 1314, - 1342, 1316, 1316, 1316, 1343, 1314, 1314, 1314, 1334, 1316, 1316, 1335, - 1344, 1345, 1345, 1345, 1345, 1345, 1345, 1346, 1346, 1346, 1346, 1346, - 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, - 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, - 1346, 1346, 1346, 1347, 1347, 1347, 1347, 1347, 1347, 1348, 1349, 1347, - 1347, 1347, 1347, 1347, 1350, 1351, 1346, 1352, 1353, 119, 1354, 1355, - 1347, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 1356, 1357, 1357, - 1358, 1359, 1360, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, + 119, 119, 789, 789, 789, 789, 789, 789, 789, 789, 789, 789, 789, 789, + 789, 789, 789, 789, 789, 789, 789, 789, 789, 789, 789, 789, 789, 789, + 789, 789, 789, 789, 789, 789, 789, 789, 789, 789, 789, 789, 789, 1271, + 1272, 1272, 1272, 1273, 1274, 1275, 119, 119, 119, 119, 119, 119, 119, + 119, 119, 119, 119, 119, 1276, 1276, 1276, 1277, 1278, 119, 1279, 1279, + 1279, 1279, 1279, 1279, 1280, 1281, 1282, 119, 1283, 1284, 1285, 1279, + 1279, 1286, 1279, 1279, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, + 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 1287, 1287, + 1287, 1287, 1288, 1288, 1288, 1288, 1289, 1289, 1290, 1291, 119, 119, + 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 1292, 1292, 1292, 1292, + 1292, 1292, 1292, 1292, 1292, 1293, 1294, 1295, 1295, 1295, 1295, 1295, + 1295, 1296, 1297, 1298, 119, 119, 119, 119, 119, 119, 119, 119, 1299, + 119, 119, 119, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, + 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, + 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, + 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, + 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, + 1300, 1300, 1300, 1300, 1300, 1300, 119, 1300, 1300, 1300, 1300, 1300, + 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, + 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, + 1300, 1301, 119, 1302, 733, 733, 733, 733, 733, 733, 733, 733, 733, 733, + 733, 733, 733, 733, 733, 733, 733, 733, 733, 733, 733, 733, 733, 733, + 733, 733, 733, 733, 733, 733, 733, 733, 733, 733, 1303, 119, 119, 119, + 119, 119, 119, 1304, 119, 1305, 119, 1306, 1306, 1306, 1306, 1306, 1306, + 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, + 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, + 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, + 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1307, 1308, 1308, 1308, 1308, + 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1309, 1308, 1310, + 1308, 1311, 1308, 1312, 1313, 119, 119, 119, 119, 119, 119, 119, 119, + 119, 119, 119, 609, 609, 609, 609, 609, 609, 609, 609, 609, 609, 609, + 609, 609, 609, 609, 609, 609, 609, 609, 609, 609, 609, 609, 609, 609, + 609, 609, 609, 609, 609, 1314, 119, 609, 609, 609, 609, 1315, 1316, 609, + 609, 609, 609, 609, 609, 1317, 1318, 1319, 1320, 1321, 1322, 609, 609, + 609, 1323, 609, 609, 609, 609, 609, 609, 609, 1324, 119, 119, 944, 944, + 944, 944, 944, 944, 944, 944, 1325, 119, 119, 119, 119, 119, 119, 119, + 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 939, 939, + 1326, 119, 580, 580, 580, 580, 580, 580, 580, 580, 580, 580, 616, 119, + 939, 939, 939, 1327, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, + 119, 119, 119, 119, 119, 119, 1328, 1328, 1328, 1329, 1330, 1330, 1331, + 1328, 1328, 1332, 1333, 1330, 1330, 1328, 1328, 1328, 1329, 1330, 1330, + 1334, 1335, 1336, 1332, 1337, 1338, 1330, 1328, 1328, 1328, 1329, 1330, + 1330, 1339, 1340, 1341, 1342, 1330, 1330, 1330, 1343, 1344, 1345, 1346, + 1330, 1330, 1331, 1328, 1328, 1332, 1330, 1330, 1330, 1328, 1328, 1328, + 1329, 1330, 1330, 1331, 1328, 1328, 1332, 1330, 1330, 1330, 1328, 1328, + 1328, 1329, 1330, 1330, 1331, 1328, 1328, 1332, 1330, 1330, 1330, 1328, + 1328, 1328, 1329, 1330, 1330, 1347, 1328, 1328, 1328, 1348, 1330, 1330, + 1349, 1350, 1328, 1328, 1351, 1330, 1330, 1352, 1331, 1328, 1328, 1353, + 1330, 1330, 1354, 1355, 1328, 1328, 1356, 1330, 1330, 1330, 1357, 1328, + 1328, 1328, 1348, 1330, 1330, 1349, 1358, 1359, 1359, 1359, 1359, 1359, + 1359, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, + 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, + 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1361, 1361, 1361, + 1361, 1361, 1361, 1362, 1363, 1361, 1361, 1361, 1361, 1361, 1364, 1365, + 1360, 1366, 1367, 119, 1368, 1369, 1361, 119, 119, 119, 119, 119, 119, + 119, 119, 119, 119, 1370, 1371, 1371, 1372, 1373, 1374, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, - 119, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, - 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, - 1361, 1362, 1363, 1364, 119, 119, 119, 119, 119, 1365, 1365, 1365, 1365, - 1366, 1367, 1367, 1367, 1368, 1369, 1370, 1371, 119, 119, 119, 119, 119, + 119, 119, 119, 119, 119, 119, 119, 119, 119, 1375, 1375, 1375, 1375, + 1375, 1376, 1377, 1378, 1379, 1380, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, - 119, 1372, 1373, 1373, 1373, 1373, 1373, 1373, 1374, 1375, 119, 119, 119, - 119, 119, 119, 119, 119, 119, 1376, 127, 127, 127, 1377, 1378, 1379, - 1380, 1381, 1382, 1377, 1383, 1377, 1379, 1379, 1384, 127, 1385, 127, - 1386, 1387, 1385, 127, 1386, 119, 119, 119, 119, 119, 119, 1388, 119, - 1389, 1390, 1390, 1390, 1390, 1391, 1390, 1390, 1390, 1390, 1390, 1390, - 1390, 1390, 1390, 1390, 1390, 1390, 1391, 1392, 1390, 1393, 1394, 1390, - 1394, 1395, 1394, 1390, 1390, 1390, 1396, 1392, 620, 1397, 622, 622, 622, - 1398, 622, 622, 622, 622, 622, 622, 622, 1399, 622, 622, 622, 1400, 1401, - 1402, 622, 1403, 1392, 1392, 1392, 1392, 1392, 1392, 1404, 1405, 1405, - 1405, 1406, 1392, 755, 755, 755, 755, 755, 1407, 755, 1408, 1409, 1392, - 1410, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, - 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 723, 723, 723, 723, 1411, - 1412, 1413, 723, 723, 723, 723, 723, 723, 723, 723, 1414, 1415, 723, - 1416, 1417, 723, 723, 1418, 1419, 1420, 1421, 1416, 1390, 723, 723, 1422, - 1423, 723, 723, 723, 723, 723, 723, 723, 1424, 1425, 1426, 1427, 723, - 1428, 1429, 1426, 1430, 1431, 723, 723, 723, 1432, 1433, 1434, 723, 723, - 723, 723, 723, 723, 723, 723, 1435, 1436, 723, 1437, 643, 1438, 723, - 1439, 1440, 581, 1441, 723, 723, 723, 1390, 1442, 1443, 1390, 1390, 1444, - 1390, 1389, 1390, 1390, 1390, 1390, 1390, 1445, 1446, 1390, 1390, 1445, - 1447, 723, 723, 723, 723, 723, 723, 723, 723, 1448, 1449, 581, 581, 581, - 581, 1450, 1451, 723, 723, 723, 723, 1452, 723, 1453, 723, 1454, 1455, - 1456, 1392, 1390, 1457, 1458, 1459, 581, 581, 581, 581, 581, 581, 581, - 581, 581, 581, 581, 581, 581, 581, 1460, 1392, 581, 581, 581, 581, 581, - 581, 581, 581, 581, 581, 1461, 1462, 1392, 1392, 1392, 1392, 581, 1460, - 581, 581, 581, 581, 581, 581, 581, 1392, 581, 1463, 581, 581, 581, 581, - 581, 1392, 581, 581, 581, 1464, 1392, 1392, 1392, 1392, 1392, 1392, 1392, - 1392, 1392, 1392, 581, 1460, 723, 1465, 1466, 723, 1426, 1467, 723, 723, - 723, 723, 723, 723, 1468, 1469, 723, 723, 723, 723, 1470, 1392, 1471, - 1472, 1470, 1392, 1473, 1474, 723, 723, 723, 723, 1392, 1392, 1392, 1392, - 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1390, 1396, 1392, 1392, - 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, - 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, - 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, - 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, - 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, - 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, - 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1475, 773, 773, 773, 773, 773, - 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, - 773, 773, 773, 773, 773, 773, 773, 1476, 775, 775, 775, 775, 775, 773, - 773, 773, 773, 773, 773, 1477, 775, 773, 773, 773, 773, 773, 773, 773, - 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, - 773, 773, 773, 773, 773, 773, 774, 773, 773, 773, 773, 773, 773, 773, + 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 1381, 1381, 1381, + 1381, 1381, 1382, 1383, 1384, 1385, 1385, 1385, 1385, 1385, 1385, 1385, + 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, + 1385, 1385, 1385, 1385, 1385, 1386, 1387, 1388, 119, 119, 119, 119, 119, + 1389, 1389, 1389, 1389, 1390, 1391, 1391, 1391, 1392, 1393, 1394, 1395, + 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, + 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, + 119, 119, 119, 119, 119, 119, 1396, 1397, 1397, 1397, 1397, 1397, 1397, + 1398, 1399, 119, 119, 119, 119, 119, 119, 119, 119, 119, 1396, 1397, + 1397, 1397, 1397, 1400, 1397, 1401, 119, 119, 119, 119, 119, 119, 119, + 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, + 119, 119, 119, 1402, 127, 127, 127, 1403, 1404, 1405, 1406, 1407, 1408, + 1403, 1409, 1403, 1405, 1405, 1410, 127, 1411, 127, 1412, 1413, 1411, + 127, 1412, 119, 119, 119, 119, 119, 119, 1414, 119, 1415, 1416, 1416, + 1416, 1416, 1417, 1416, 1416, 1416, 1416, 1416, 1416, 1416, 1416, 1416, + 1416, 1416, 1416, 1417, 1418, 1416, 1419, 1420, 1416, 1420, 1421, 1420, + 1416, 1416, 1416, 1422, 1418, 619, 1423, 621, 621, 621, 1424, 621, 621, + 621, 621, 621, 621, 621, 1425, 621, 621, 621, 1426, 1427, 1428, 621, + 1429, 1418, 1418, 1418, 1418, 1418, 1418, 1430, 1431, 1431, 1431, 1432, + 1418, 753, 753, 753, 753, 753, 1433, 753, 1434, 1435, 1418, 1436, 1418, + 1418, 1418, 1418, 1418, 1418, 1418, 1418, 1418, 1418, 1418, 1418, 1418, + 1418, 1418, 1418, 1418, 1418, 1418, 721, 721, 721, 721, 1437, 1438, 1439, + 721, 721, 721, 721, 721, 721, 721, 721, 1440, 1441, 721, 1442, 1443, 721, + 721, 1444, 1445, 1446, 1447, 1442, 1416, 721, 721, 1448, 1449, 721, 721, + 721, 721, 721, 721, 721, 1450, 1451, 1452, 1453, 721, 1454, 1452, 1452, + 1455, 1456, 1457, 1458, 721, 1459, 1460, 1461, 721, 721, 721, 721, 721, + 721, 721, 721, 1462, 1463, 721, 1464, 642, 1465, 721, 1466, 1467, 580, + 1468, 721, 721, 721, 1416, 1469, 1470, 1416, 1416, 1471, 1416, 1415, + 1416, 1416, 1416, 1416, 1416, 1472, 1473, 1416, 1416, 1472, 1474, 721, + 721, 721, 721, 721, 721, 721, 721, 1475, 1476, 580, 580, 580, 580, 1477, + 1478, 721, 721, 721, 721, 1479, 721, 1480, 721, 1481, 1482, 1483, 1418, + 1416, 1484, 1485, 1486, 580, 580, 580, 580, 580, 580, 580, 580, 580, 580, + 580, 580, 580, 580, 1487, 1418, 580, 580, 580, 580, 580, 580, 580, 580, + 580, 580, 1488, 1489, 721, 1490, 1418, 1418, 580, 1487, 580, 580, 580, + 580, 580, 580, 580, 1418, 580, 1491, 580, 580, 580, 580, 580, 1418, 580, + 580, 580, 1492, 1418, 1418, 1418, 1418, 1418, 1418, 1418, 1418, 1418, + 1418, 580, 1493, 721, 1452, 1494, 721, 1452, 1495, 721, 721, 721, 721, + 721, 721, 1496, 1497, 721, 721, 721, 721, 1498, 1499, 1500, 1501, 721, + 1502, 1503, 1504, 721, 721, 721, 721, 580, 580, 580, 580, 580, 580, 580, + 580, 580, 580, 1487, 1418, 1416, 1422, 1490, 1486, 1486, 1418, 1436, + 1418, 1418, 1418, 1418, 1418, 1418, 1418, 1418, 1418, 1418, 1418, 1418, + 1418, 1418, 1418, 1418, 1418, 1418, 1418, 1418, 1418, 1418, 1418, 1418, + 1418, 1418, 1418, 1418, 1418, 1418, 1418, 1418, 1418, 1418, 1418, 1418, + 1418, 1418, 1418, 1418, 1418, 1418, 1418, 1418, 1418, 1418, 1418, 1418, + 1418, 1418, 1418, 1418, 1418, 1418, 1418, 1418, 1418, 1418, 1418, 1418, + 1418, 1418, 1418, 1418, 1418, 1418, 1418, 1418, 1418, 1418, 1418, 1418, + 1418, 1418, 1418, 1418, 1505, 771, 771, 771, 771, 771, 771, 771, 771, + 771, 771, 771, 771, 771, 771, 771, 771, 771, 771, 771, 771, 771, 771, + 771, 771, 771, 771, 1506, 773, 773, 773, 773, 773, 771, 771, 771, 771, + 771, 771, 1507, 773, 771, 771, 771, 771, 771, 771, 771, 771, 771, 771, + 771, 771, 771, 771, 771, 771, 771, 771, 771, 771, 771, 771, 771, 771, + 771, 771, 771, 772, 771, 771, 771, 771, 771, 771, 771, 771, 771, 771, + 771, 771, 771, 771, 771, 771, 771, 771, 771, 771, 771, 771, 771, 771, + 771, 771, 771, 771, 771, 771, 771, 771, 771, 771, 771, 771, 771, 771, + 771, 771, 771, 771, 771, 771, 771, 771, 771, 771, 881, 773, 771, 771, + 771, 771, 771, 771, 771, 771, 771, 771, 771, 771, 771, 771, 771, 771, + 771, 771, 771, 771, 771, 771, 771, 771, 771, 771, 771, 771, 771, 771, + 771, 771, 771, 771, 771, 771, 771, 771, 1508, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, - 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 883, - 775, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, + 773, 773, 771, 771, 771, 772, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, - 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 1478, 775, 775, - 775, 775, 775, 775, 775, 775, 775, 775, 775, 775, 775, 775, 775, 775, - 775, 775, 775, 775, 775, 775, 775, 775, 775, 775, 775, 775, 775, 775, - 775, 775, 775, 775, 775, 773, 773, 773, 774, 775, 775, 775, 775, 775, - 775, 775, 775, 775, 775, 775, 775, 775, 775, 775, 775, 775, 775, 775, - 775, 775, 775, 775, 775, 775, 775, 775, 775, 775, 775, 775, 775, 775, - 775, 775, 775, 775, 775, 775, 775, 775, 775, 775, 775, 775, 775, 775, - 775, 775, 775, 775, 775, 775, 775, 775, 775, 775, 775, 775, 1479, 1480, - 119, 119, 119, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, - 1481, 1481, 1481, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, - 119, 119, 119, 119, 119, 898, 898, 898, 898, 898, 898, 898, 898, 898, - 898, 898, 898, 898, 898, 898, 898, 898, 898, 898, 898, 898, 898, 898, - 898, 898, 898, 898, 898, 898, 898, 119, 119, 882, 882, 882, 882, 882, - 882, 882, 882, 882, 882, 882, 882, 882, 882, 882, 882, 882, 882, 882, - 882, 882, 882, 882, 882, 882, 882, 882, 882, 882, 882, 882, 1482, + 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, + 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, + 773, 773, 773, 773, 773, 773, 773, 773, 773, 1509, 1510, 119, 119, 119, + 1511, 1511, 1511, 1511, 1511, 1511, 1511, 1511, 1511, 1511, 1511, 1511, + 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, 119, + 119, 119, 896, 896, 896, 896, 896, 896, 896, 896, 896, 896, 896, 896, + 896, 896, 896, 896, 896, 896, 896, 896, 896, 896, 896, 896, 896, 896, + 896, 896, 896, 896, 119, 119, 880, 880, 880, 880, 880, 880, 880, 880, + 880, 880, 880, 880, 880, 880, 880, 880, 880, 880, 880, 880, 880, 880, + 880, 880, 880, 880, 880, 880, 880, 880, 880, 1512, }; static const unsigned short index2[] = { @@ -2868,704 +2910,722 @@ static const unsigned short index2[] = { 139, 139, 139, 157, 139, 139, 157, 157, 139, 139, 139, 139, 139, 193, 193, 193, 194, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 195, 193, 194, 196, 195, 194, 194, 194, 193, 193, 193, 193, - 193, 193, 193, 193, 194, 194, 194, 194, 197, 194, 194, 195, 96, 156, 198, - 198, 193, 193, 193, 195, 195, 193, 193, 199, 199, 200, 200, 200, 200, - 200, 200, 200, 200, 200, 200, 201, 202, 195, 195, 195, 195, 195, 195, - 203, 204, 205, 205, 81, 203, 203, 203, 203, 203, 203, 203, 203, 81, 81, - 203, 203, 81, 81, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, 203, - 203, 203, 203, 81, 203, 203, 203, 203, 203, 203, 203, 81, 203, 81, 81, - 81, 203, 203, 203, 203, 81, 81, 206, 203, 205, 205, 205, 204, 204, 204, - 204, 81, 81, 205, 205, 81, 81, 205, 205, 207, 203, 81, 81, 81, 81, 81, - 81, 81, 81, 205, 81, 81, 81, 81, 203, 203, 81, 203, 203, 203, 204, 204, - 81, 81, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 203, 203, 209, - 209, 210, 210, 210, 210, 210, 211, 212, 213, 203, 214, 215, 81, 81, 216, - 216, 217, 81, 218, 218, 218, 218, 218, 218, 81, 81, 81, 81, 218, 218, 81, - 81, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, 218, - 81, 218, 218, 218, 218, 218, 218, 218, 81, 218, 218, 81, 218, 218, 81, - 218, 218, 81, 81, 219, 81, 217, 217, 217, 216, 216, 81, 81, 81, 81, 216, - 216, 81, 81, 216, 216, 220, 81, 81, 81, 216, 81, 81, 81, 81, 81, 81, 81, - 218, 218, 218, 218, 81, 218, 81, 81, 81, 81, 81, 81, 81, 221, 221, 221, - 221, 221, 221, 221, 221, 221, 221, 216, 216, 218, 218, 218, 216, 222, 81, - 81, 223, 223, 224, 81, 225, 225, 225, 225, 225, 225, 225, 225, 225, 81, - 225, 225, 225, 81, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, 225, - 225, 225, 225, 81, 225, 225, 225, 225, 225, 225, 225, 81, 225, 225, 81, - 225, 225, 225, 225, 225, 81, 81, 226, 225, 224, 224, 224, 223, 223, 223, - 223, 223, 81, 223, 223, 224, 81, 224, 224, 227, 81, 81, 225, 81, 81, 81, - 81, 81, 81, 81, 225, 225, 223, 223, 81, 81, 228, 228, 228, 228, 228, 228, - 228, 228, 228, 228, 229, 230, 81, 81, 81, 81, 81, 81, 81, 225, 223, 223, - 223, 223, 223, 223, 81, 231, 232, 232, 81, 233, 233, 233, 233, 233, 233, - 233, 233, 81, 81, 233, 233, 81, 81, 233, 233, 233, 233, 233, 233, 233, - 233, 233, 233, 233, 233, 233, 233, 81, 233, 233, 233, 233, 233, 233, 233, - 81, 233, 233, 81, 233, 233, 233, 233, 233, 81, 81, 234, 233, 232, 231, - 232, 231, 231, 231, 231, 81, 81, 232, 232, 81, 81, 232, 232, 235, 81, 81, - 81, 81, 81, 81, 81, 81, 231, 232, 81, 81, 81, 81, 233, 233, 81, 233, 233, - 233, 231, 231, 81, 81, 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, - 237, 233, 238, 238, 238, 238, 238, 238, 81, 81, 239, 240, 81, 240, 240, - 240, 240, 240, 240, 81, 81, 81, 240, 240, 240, 81, 240, 240, 240, 240, - 81, 81, 81, 240, 240, 81, 240, 81, 240, 240, 81, 81, 81, 240, 240, 81, - 81, 81, 240, 240, 240, 240, 240, 240, 240, 240, 240, 240, 81, 81, 81, 81, - 241, 241, 239, 241, 241, 81, 81, 81, 241, 241, 241, 81, 241, 241, 241, - 242, 81, 81, 240, 81, 81, 81, 81, 81, 81, 241, 81, 81, 81, 81, 81, 81, - 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, 244, 244, 244, 245, - 245, 245, 245, 245, 245, 246, 245, 81, 81, 81, 81, 81, 247, 248, 248, - 248, 247, 249, 249, 249, 249, 249, 249, 249, 249, 81, 249, 249, 249, 81, - 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, - 249, 249, 81, 81, 81, 249, 247, 247, 247, 248, 248, 248, 248, 81, 247, - 247, 247, 81, 247, 247, 247, 250, 81, 81, 81, 81, 81, 81, 81, 251, 252, - 81, 249, 249, 249, 81, 81, 81, 81, 81, 249, 249, 247, 247, 81, 81, 253, - 253, 253, 253, 253, 253, 253, 253, 253, 253, 254, 254, 254, 254, 254, - 254, 254, 255, 256, 257, 258, 258, 259, 256, 256, 256, 256, 256, 256, - 256, 256, 81, 256, 256, 256, 81, 256, 256, 256, 256, 256, 256, 256, 256, - 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 81, 256, 256, 256, 256, - 256, 81, 81, 260, 256, 258, 261, 258, 258, 258, 258, 258, 81, 261, 258, - 258, 81, 258, 258, 257, 262, 81, 81, 81, 81, 81, 81, 81, 258, 258, 81, - 81, 81, 81, 81, 81, 81, 256, 81, 256, 256, 257, 257, 81, 81, 263, 263, - 263, 263, 263, 263, 263, 263, 263, 263, 81, 256, 256, 81, 81, 81, 81, 81, - 264, 264, 265, 265, 81, 266, 266, 266, 266, 266, 266, 266, 266, 81, 266, - 266, 266, 81, 266, 266, 266, 266, 266, 266, 266, 266, 266, 266, 266, 266, - 266, 266, 266, 266, 266, 267, 267, 266, 265, 265, 265, 264, 264, 264, - 264, 81, 265, 265, 265, 81, 265, 265, 265, 267, 266, 268, 81, 81, 81, 81, - 266, 266, 266, 265, 269, 269, 269, 269, 269, 269, 269, 266, 266, 266, - 264, 264, 81, 81, 270, 270, 270, 270, 270, 270, 270, 270, 270, 270, 269, - 269, 269, 269, 269, 269, 269, 269, 269, 271, 266, 266, 266, 266, 266, - 266, 81, 81, 272, 272, 81, 273, 273, 273, 273, 273, 273, 273, 273, 273, - 273, 273, 273, 273, 273, 273, 273, 273, 273, 81, 81, 81, 273, 273, 273, - 273, 273, 273, 273, 273, 81, 273, 273, 273, 273, 273, 273, 273, 273, 273, - 81, 273, 81, 81, 81, 81, 274, 81, 81, 81, 81, 272, 272, 272, 275, 275, - 275, 81, 275, 81, 272, 272, 272, 272, 272, 272, 272, 272, 81, 81, 81, 81, - 81, 81, 276, 276, 276, 276, 276, 276, 276, 276, 276, 276, 81, 81, 272, - 272, 277, 81, 81, 81, 81, 278, 278, 278, 278, 278, 278, 278, 278, 278, - 278, 278, 278, 278, 278, 278, 278, 279, 278, 278, 279, 279, 279, 279, - 280, 280, 281, 81, 81, 81, 81, 282, 278, 278, 278, 278, 278, 278, 283, - 279, 284, 284, 284, 284, 279, 279, 279, 285, 286, 286, 286, 286, 286, - 286, 286, 286, 286, 286, 287, 287, 81, 81, 81, 81, 81, 288, 288, 81, 288, - 81, 81, 288, 288, 81, 288, 81, 81, 288, 81, 81, 81, 81, 81, 81, 288, 288, - 288, 288, 81, 288, 288, 288, 288, 288, 288, 288, 81, 288, 288, 288, 81, - 288, 81, 288, 81, 81, 288, 288, 81, 288, 288, 288, 288, 289, 288, 288, - 289, 289, 289, 289, 290, 290, 81, 289, 289, 288, 81, 81, 288, 288, 288, - 288, 288, 81, 291, 81, 292, 292, 292, 292, 289, 289, 81, 81, 293, 293, - 293, 293, 293, 293, 293, 293, 293, 293, 81, 81, 288, 288, 288, 288, 294, - 295, 295, 295, 296, 297, 296, 296, 298, 296, 296, 299, 298, 300, 300, - 300, 300, 300, 298, 301, 300, 301, 301, 301, 302, 302, 301, 301, 301, - 301, 301, 301, 303, 303, 303, 303, 303, 303, 303, 303, 303, 303, 304, - 304, 304, 304, 304, 304, 304, 304, 304, 304, 305, 302, 301, 302, 301, - 306, 307, 308, 307, 308, 309, 309, 294, 294, 294, 294, 294, 294, 294, - 294, 81, 294, 294, 294, 294, 294, 294, 294, 294, 294, 294, 294, 294, 81, - 81, 81, 81, 310, 311, 312, 313, 312, 312, 312, 312, 312, 311, 311, 311, - 311, 312, 314, 311, 312, 315, 315, 316, 299, 315, 315, 294, 294, 294, - 294, 294, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 81, 312, - 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 81, 305, 305, 301, - 301, 301, 301, 301, 301, 302, 301, 301, 301, 301, 301, 301, 81, 301, 301, - 296, 296, 299, 296, 297, 317, 317, 317, 317, 298, 298, 81, 81, 81, 81, - 81, 318, 318, 318, 318, 318, 318, 318, 318, 318, 318, 318, 319, 319, 320, - 320, 320, 320, 319, 320, 320, 320, 320, 320, 321, 319, 322, 322, 319, - 319, 320, 320, 318, 323, 323, 323, 323, 323, 323, 323, 323, 323, 323, - 324, 324, 325, 325, 325, 325, 318, 318, 318, 318, 318, 318, 319, 319, - 320, 320, 318, 318, 318, 318, 320, 320, 320, 318, 319, 319, 319, 318, - 318, 319, 319, 319, 319, 319, 319, 319, 318, 318, 318, 320, 320, 320, - 320, 318, 318, 318, 318, 318, 320, 319, 319, 320, 320, 319, 319, 319, - 319, 319, 319, 326, 318, 319, 323, 323, 319, 319, 319, 320, 327, 327, - 328, 328, 328, 328, 328, 328, 328, 328, 328, 328, 328, 328, 328, 328, 81, - 328, 81, 81, 81, 81, 81, 328, 81, 81, 329, 329, 329, 329, 329, 329, 329, - 329, 329, 329, 329, 330, 331, 329, 329, 329, 332, 332, 332, 332, 332, - 332, 332, 332, 333, 333, 333, 333, 333, 333, 333, 333, 334, 334, 334, - 334, 334, 334, 334, 334, 335, 335, 335, 335, 335, 335, 335, 335, 335, 81, - 335, 335, 335, 335, 81, 81, 335, 335, 335, 335, 335, 335, 335, 81, 335, - 335, 335, 81, 81, 336, 336, 336, 337, 338, 337, 337, 337, 337, 337, 337, - 337, 339, 339, 339, 339, 339, 339, 339, 339, 339, 339, 339, 339, 339, - 339, 339, 339, 339, 339, 339, 339, 81, 81, 81, 340, 340, 340, 340, 340, - 340, 340, 340, 340, 340, 81, 81, 81, 81, 81, 81, 341, 341, 341, 341, 341, - 341, 341, 341, 341, 341, 341, 341, 341, 341, 81, 81, 342, 342, 342, 342, - 342, 342, 81, 81, 343, 344, 344, 344, 344, 344, 344, 344, 344, 344, 344, - 344, 344, 344, 344, 344, 344, 344, 344, 344, 344, 345, 345, 344, 346, - 347, 347, 347, 347, 347, 347, 347, 347, 347, 347, 347, 347, 347, 347, - 347, 347, 347, 347, 348, 349, 81, 81, 81, 350, 350, 350, 350, 350, 350, - 350, 350, 350, 350, 350, 199, 199, 199, 351, 351, 351, 350, 350, 350, - 350, 350, 350, 350, 350, 81, 81, 81, 81, 81, 81, 81, 352, 352, 352, 352, - 352, 352, 352, 352, 352, 352, 352, 352, 352, 81, 352, 352, 352, 352, 353, - 353, 354, 81, 81, 81, 355, 355, 355, 355, 355, 355, 355, 355, 355, 355, - 356, 356, 357, 199, 199, 81, 358, 358, 358, 358, 358, 358, 358, 358, 358, - 358, 359, 359, 81, 81, 81, 81, 360, 360, 360, 360, 360, 360, 360, 360, - 360, 360, 360, 360, 360, 81, 360, 360, 360, 81, 361, 361, 81, 81, 81, 81, - 362, 362, 362, 362, 362, 362, 362, 362, 362, 362, 362, 362, 363, 363, - 364, 363, 363, 363, 363, 363, 363, 363, 364, 364, 364, 364, 364, 364, - 364, 364, 363, 364, 364, 363, 363, 363, 363, 363, 363, 363, 363, 363, - 365, 363, 366, 366, 367, 368, 366, 369, 366, 370, 362, 371, 81, 81, 372, - 372, 372, 372, 372, 372, 372, 372, 372, 372, 81, 81, 81, 81, 81, 81, 373, - 373, 373, 373, 373, 373, 373, 373, 373, 373, 81, 81, 81, 81, 81, 81, 374, - 374, 375, 375, 376, 377, 378, 374, 379, 379, 374, 380, 380, 380, 381, 81, - 382, 382, 382, 382, 382, 382, 382, 382, 382, 382, 81, 81, 81, 81, 81, 81, - 383, 383, 383, 383, 383, 383, 383, 383, 383, 383, 383, 384, 383, 383, - 383, 383, 383, 81, 81, 81, 81, 81, 81, 81, 383, 383, 383, 383, 383, 380, - 380, 383, 383, 385, 383, 81, 81, 81, 81, 81, 344, 344, 344, 344, 344, - 344, 81, 81, 386, 386, 386, 386, 386, 386, 386, 386, 386, 386, 386, 386, - 386, 386, 386, 81, 387, 387, 387, 388, 388, 388, 388, 387, 387, 388, 388, - 388, 81, 81, 81, 81, 388, 388, 387, 388, 388, 388, 388, 388, 388, 389, - 390, 391, 81, 81, 81, 81, 392, 81, 81, 81, 393, 393, 394, 394, 394, 394, - 394, 394, 394, 394, 394, 394, 395, 395, 395, 395, 395, 395, 395, 395, - 395, 395, 395, 395, 395, 395, 81, 81, 395, 395, 395, 395, 395, 81, 81, - 81, 396, 396, 396, 396, 396, 396, 396, 396, 396, 396, 396, 396, 81, 81, - 81, 81, 396, 396, 81, 81, 81, 81, 81, 81, 397, 397, 397, 397, 397, 397, - 397, 397, 397, 397, 398, 81, 81, 81, 399, 399, 400, 400, 400, 400, 400, - 400, 400, 400, 401, 401, 401, 401, 401, 401, 401, 401, 401, 401, 401, - 401, 401, 401, 401, 402, 403, 404, 404, 405, 81, 81, 406, 406, 407, 407, - 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 408, 409, 408, - 409, 409, 409, 409, 409, 409, 409, 81, 410, 408, 409, 408, 408, 409, 409, - 409, 409, 409, 409, 409, 409, 408, 408, 408, 408, 408, 408, 409, 409, - 411, 411, 411, 411, 411, 411, 411, 411, 81, 81, 412, 413, 413, 413, 413, - 413, 413, 413, 413, 413, 413, 81, 81, 81, 81, 81, 81, 414, 414, 414, 414, - 414, 414, 414, 415, 414, 414, 414, 414, 414, 414, 81, 81, 96, 96, 96, 96, - 96, 156, 156, 156, 156, 156, 156, 96, 96, 156, 416, 81, 417, 417, 417, - 417, 418, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, - 419, 419, 419, 420, 418, 417, 417, 417, 417, 417, 418, 417, 418, 418, - 418, 418, 418, 417, 418, 421, 419, 419, 419, 419, 419, 419, 419, 81, 81, - 81, 81, 422, 422, 422, 422, 422, 422, 422, 422, 422, 422, 423, 423, 424, - 423, 423, 423, 423, 425, 425, 425, 425, 425, 425, 425, 425, 425, 425, - 426, 427, 426, 426, 426, 426, 426, 426, 426, 425, 425, 425, 425, 425, - 425, 425, 425, 425, 81, 81, 81, 428, 428, 429, 430, 430, 430, 430, 430, - 430, 430, 430, 430, 430, 430, 430, 430, 430, 429, 428, 428, 428, 428, - 429, 429, 428, 428, 431, 432, 428, 428, 430, 430, 433, 433, 433, 433, - 433, 433, 433, 433, 433, 433, 430, 430, 430, 430, 430, 430, 434, 434, - 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 435, 436, - 437, 437, 436, 436, 436, 437, 436, 437, 437, 437, 438, 438, 81, 81, 81, - 81, 81, 81, 81, 81, 439, 439, 439, 439, 440, 440, 440, 440, 440, 440, - 440, 440, 440, 440, 440, 440, 441, 441, 441, 441, 441, 441, 441, 441, - 442, 442, 442, 442, 442, 442, 442, 442, 441, 441, 442, 443, 81, 81, 81, - 444, 444, 444, 444, 444, 445, 445, 445, 445, 445, 445, 445, 445, 445, - 445, 81, 81, 81, 440, 440, 440, 446, 446, 446, 446, 446, 446, 446, 446, - 446, 446, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, - 447, 447, 448, 448, 448, 448, 448, 448, 449, 449, 93, 81, 81, 81, 81, 81, - 81, 81, 328, 328, 328, 81, 81, 328, 328, 328, 450, 450, 450, 450, 450, - 450, 450, 450, 96, 96, 96, 330, 451, 156, 156, 156, 156, 156, 96, 96, - 156, 156, 156, 156, 96, 452, 451, 451, 451, 451, 451, 451, 451, 453, 453, - 453, 453, 156, 453, 453, 453, 453, 452, 452, 96, 453, 453, 452, 96, 96, - 81, 81, 81, 81, 81, 81, 56, 56, 56, 56, 56, 56, 79, 79, 79, 79, 79, 93, - 59, 59, 59, 59, 59, 59, 59, 59, 59, 82, 82, 82, 82, 82, 59, 59, 59, 59, - 82, 82, 82, 82, 82, 56, 56, 56, 56, 56, 454, 56, 56, 56, 56, 56, 56, 56, - 56, 56, 56, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 82, 96, 96, - 156, 96, 96, 96, 96, 96, 96, 96, 156, 96, 96, 455, 456, 156, 457, 96, 96, - 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, - 96, 458, 459, 459, 156, 81, 96, 460, 156, 96, 156, 52, 56, 52, 56, 52, - 56, 56, 56, 56, 56, 56, 56, 56, 56, 52, 56, 79, 79, 79, 79, 79, 79, 79, - 79, 78, 78, 78, 78, 78, 78, 78, 78, 79, 79, 79, 79, 79, 79, 81, 81, 78, - 78, 78, 78, 78, 78, 81, 81, 81, 78, 81, 78, 81, 78, 81, 78, 461, 461, - 461, 461, 461, 461, 461, 461, 79, 79, 79, 79, 79, 81, 79, 79, 78, 78, 78, - 78, 461, 80, 79, 80, 80, 80, 79, 79, 79, 81, 79, 79, 78, 78, 78, 78, 461, - 80, 80, 80, 79, 79, 79, 79, 81, 81, 79, 79, 78, 78, 78, 78, 81, 80, 80, - 80, 78, 78, 78, 78, 78, 80, 80, 80, 81, 81, 79, 79, 79, 81, 79, 79, 78, - 78, 78, 78, 461, 462, 80, 81, 463, 463, 463, 463, 463, 463, 463, 464, - 463, 463, 463, 465, 466, 467, 468, 469, 470, 471, 472, 470, 473, 474, 38, - 84, 475, 476, 477, 42, 475, 476, 477, 42, 38, 38, 478, 84, 479, 479, 479, - 480, 481, 482, 483, 484, 485, 486, 487, 33, 488, 489, 488, 488, 489, 490, - 491, 491, 84, 42, 50, 38, 492, 492, 478, 493, 493, 84, 84, 84, 494, 477, - 495, 492, 492, 492, 84, 84, 84, 84, 84, 84, 84, 84, 496, 84, 493, 84, - 377, 84, 377, 377, 377, 377, 84, 377, 377, 463, 497, 498, 498, 498, 498, - 81, 499, 500, 501, 502, 503, 503, 503, 503, 503, 503, 504, 59, 81, 81, - 47, 504, 504, 504, 504, 504, 505, 505, 496, 477, 495, 506, 504, 47, 47, - 47, 47, 504, 504, 504, 504, 504, 505, 505, 496, 477, 495, 81, 59, 59, 59, - 59, 59, 81, 81, 81, 282, 282, 282, 282, 282, 282, 282, 507, 282, 508, - 282, 282, 36, 282, 282, 282, 282, 282, 282, 282, 282, 282, 507, 282, 282, - 282, 282, 507, 282, 282, 507, 282, 509, 509, 509, 509, 509, 509, 509, - 509, 96, 96, 451, 451, 96, 96, 96, 96, 451, 451, 451, 96, 96, 416, 416, - 416, 416, 96, 416, 416, 416, 451, 451, 96, 156, 96, 451, 451, 156, 156, - 156, 156, 96, 81, 81, 81, 81, 81, 81, 81, 40, 40, 510, 511, 40, 512, 40, - 510, 40, 511, 49, 510, 510, 510, 49, 49, 510, 510, 510, 513, 40, 510, - 514, 40, 496, 510, 510, 510, 510, 510, 40, 40, 40, 512, 512, 40, 510, 40, - 85, 40, 510, 40, 52, 515, 510, 510, 516, 49, 510, 510, 52, 510, 49, 453, - 453, 453, 453, 49, 40, 40, 49, 49, 510, 510, 496, 496, 496, 496, 496, - 510, 49, 49, 49, 49, 40, 496, 40, 40, 56, 317, 517, 517, 517, 518, 51, - 519, 517, 517, 517, 517, 517, 51, 518, 518, 51, 517, 520, 520, 520, 520, - 520, 520, 520, 520, 520, 520, 520, 520, 521, 521, 521, 521, 520, 520, - 521, 521, 521, 521, 521, 521, 521, 521, 521, 52, 56, 521, 521, 521, 521, - 51, 40, 40, 81, 81, 81, 81, 54, 54, 54, 54, 54, 512, 512, 512, 512, 512, - 496, 496, 40, 40, 40, 40, 496, 40, 40, 496, 40, 40, 496, 40, 40, 40, 40, - 40, 40, 40, 496, 40, 40, 40, 40, 40, 40, 40, 40, 40, 44, 44, 40, 40, 40, - 40, 40, 40, 40, 40, 40, 40, 40, 40, 496, 496, 40, 40, 54, 40, 54, 40, 40, - 40, 40, 40, 40, 40, 40, 40, 40, 44, 40, 40, 40, 40, 496, 496, 496, 496, - 496, 496, 496, 496, 496, 496, 496, 496, 54, 496, 54, 54, 496, 496, 496, - 54, 54, 496, 496, 54, 496, 496, 496, 54, 496, 54, 522, 523, 496, 54, 496, - 496, 496, 496, 54, 496, 496, 54, 54, 54, 54, 496, 496, 54, 496, 54, 496, - 54, 54, 54, 54, 54, 54, 496, 54, 496, 496, 496, 496, 496, 54, 54, 54, 54, - 496, 496, 496, 496, 54, 54, 496, 496, 54, 496, 496, 496, 54, 496, 496, - 496, 496, 496, 54, 496, 496, 496, 496, 496, 54, 54, 496, 496, 54, 54, 54, - 54, 496, 496, 54, 54, 496, 496, 54, 54, 496, 496, 496, 496, 496, 54, 496, - 496, 496, 54, 496, 496, 496, 496, 496, 496, 496, 496, 496, 496, 496, 496, - 496, 54, 496, 496, 496, 496, 496, 496, 496, 524, 477, 495, 477, 495, 40, - 40, 40, 40, 40, 40, 512, 40, 40, 40, 40, 40, 40, 40, 525, 525, 40, 40, - 40, 40, 496, 496, 40, 40, 40, 40, 40, 40, 40, 526, 527, 40, 40, 40, 40, - 40, 40, 40, 40, 40, 40, 40, 317, 317, 317, 317, 317, 317, 317, 317, 317, - 317, 317, 317, 317, 40, 496, 40, 40, 40, 40, 40, 40, 40, 40, 317, 40, 40, - 40, 40, 40, 496, 496, 496, 496, 496, 496, 496, 496, 496, 40, 40, 40, 40, - 40, 528, 528, 528, 528, 40, 40, 40, 525, 529, 529, 525, 40, 40, 40, 40, + 193, 193, 193, 193, 194, 194, 194, 194, 197, 194, 194, 195, 96, 156, 96, + 96, 193, 193, 193, 195, 195, 193, 193, 198, 198, 199, 199, 199, 199, 199, + 199, 199, 199, 199, 199, 200, 201, 195, 195, 195, 195, 195, 195, 202, + 203, 204, 204, 81, 202, 202, 202, 202, 202, 202, 202, 202, 81, 81, 202, + 202, 81, 81, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, 202, + 202, 202, 81, 202, 202, 202, 202, 202, 202, 202, 81, 202, 81, 81, 81, + 202, 202, 202, 202, 81, 81, 205, 202, 204, 204, 204, 203, 203, 203, 203, + 81, 81, 204, 204, 81, 81, 204, 204, 206, 202, 81, 81, 81, 81, 81, 81, 81, + 81, 204, 81, 81, 81, 81, 202, 202, 81, 202, 202, 202, 203, 203, 81, 81, + 207, 207, 207, 207, 207, 207, 207, 207, 207, 207, 202, 202, 208, 208, + 209, 209, 209, 209, 209, 210, 211, 212, 202, 213, 214, 81, 81, 215, 215, + 216, 81, 217, 217, 217, 217, 217, 217, 81, 81, 81, 81, 217, 217, 81, 81, + 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 217, 81, + 217, 217, 217, 217, 217, 217, 217, 81, 217, 217, 81, 217, 217, 81, 217, + 217, 81, 81, 218, 81, 216, 216, 216, 215, 215, 81, 81, 81, 81, 215, 215, + 81, 81, 215, 215, 219, 81, 81, 81, 215, 81, 81, 81, 81, 81, 81, 81, 217, + 217, 217, 217, 81, 217, 81, 81, 81, 81, 81, 81, 81, 220, 220, 220, 220, + 220, 220, 220, 220, 220, 220, 215, 215, 217, 217, 217, 215, 221, 81, 81, + 222, 222, 223, 81, 224, 224, 224, 224, 224, 224, 224, 224, 224, 81, 224, + 224, 224, 81, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, 224, + 224, 224, 81, 224, 224, 224, 224, 224, 224, 224, 81, 224, 224, 81, 224, + 224, 224, 224, 224, 81, 81, 225, 224, 223, 223, 223, 222, 222, 222, 222, + 222, 81, 222, 222, 223, 81, 223, 223, 226, 81, 81, 224, 81, 81, 81, 81, + 81, 81, 81, 224, 224, 222, 222, 81, 81, 227, 227, 227, 227, 227, 227, + 227, 227, 227, 227, 228, 229, 81, 81, 81, 81, 81, 81, 81, 224, 222, 222, + 222, 222, 222, 222, 81, 230, 231, 231, 81, 232, 232, 232, 232, 232, 232, + 232, 232, 81, 81, 232, 232, 81, 81, 232, 232, 232, 232, 232, 232, 232, + 232, 232, 232, 232, 232, 232, 232, 81, 232, 232, 232, 232, 232, 232, 232, + 81, 232, 232, 81, 232, 232, 232, 232, 232, 81, 81, 233, 232, 231, 230, + 231, 230, 230, 230, 230, 81, 81, 231, 231, 81, 81, 231, 231, 234, 81, 81, + 81, 81, 81, 81, 81, 81, 230, 231, 81, 81, 81, 81, 232, 232, 81, 232, 232, + 232, 230, 230, 81, 81, 235, 235, 235, 235, 235, 235, 235, 235, 235, 235, + 236, 232, 237, 237, 237, 237, 237, 237, 81, 81, 238, 239, 81, 239, 239, + 239, 239, 239, 239, 81, 81, 81, 239, 239, 239, 81, 239, 239, 239, 239, + 81, 81, 81, 239, 239, 81, 239, 81, 239, 239, 81, 81, 81, 239, 239, 81, + 81, 81, 239, 239, 239, 239, 239, 239, 239, 239, 239, 239, 81, 81, 81, 81, + 240, 240, 238, 240, 240, 81, 81, 81, 240, 240, 240, 81, 240, 240, 240, + 241, 81, 81, 239, 81, 81, 81, 81, 81, 81, 240, 81, 81, 81, 81, 81, 81, + 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 243, 243, 243, 244, + 244, 244, 244, 244, 244, 245, 244, 81, 81, 81, 81, 81, 246, 247, 247, + 247, 246, 248, 248, 248, 248, 248, 248, 248, 248, 81, 248, 248, 248, 81, + 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, + 248, 248, 81, 81, 81, 248, 246, 246, 246, 247, 247, 247, 247, 81, 246, + 246, 246, 81, 246, 246, 246, 249, 81, 81, 81, 81, 81, 81, 81, 250, 251, + 81, 248, 248, 248, 81, 81, 81, 81, 81, 248, 248, 246, 246, 81, 81, 252, + 252, 252, 252, 252, 252, 252, 252, 252, 252, 81, 81, 81, 81, 81, 81, 81, + 253, 254, 254, 254, 254, 254, 254, 254, 255, 256, 257, 258, 258, 259, + 256, 256, 256, 256, 256, 256, 256, 256, 81, 256, 256, 256, 81, 256, 256, + 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, + 256, 256, 81, 256, 256, 256, 256, 256, 81, 81, 260, 256, 258, 261, 258, + 258, 258, 258, 258, 81, 261, 258, 258, 81, 258, 258, 257, 262, 81, 81, + 81, 81, 81, 81, 81, 258, 258, 81, 81, 81, 81, 81, 81, 81, 256, 81, 256, + 256, 257, 257, 81, 81, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, + 81, 256, 256, 81, 81, 81, 81, 81, 264, 264, 265, 265, 81, 266, 266, 266, + 266, 266, 266, 266, 266, 81, 266, 266, 266, 81, 266, 266, 266, 266, 266, + 266, 266, 266, 266, 266, 266, 266, 266, 266, 266, 266, 266, 267, 267, + 266, 265, 265, 265, 264, 264, 264, 264, 81, 265, 265, 265, 81, 265, 265, + 265, 267, 266, 268, 81, 81, 81, 81, 266, 266, 266, 265, 269, 269, 269, + 269, 269, 269, 269, 266, 266, 266, 264, 264, 81, 81, 270, 270, 270, 270, + 270, 270, 270, 270, 270, 270, 269, 269, 269, 269, 269, 269, 269, 269, + 269, 271, 266, 266, 266, 266, 266, 266, 81, 81, 272, 272, 81, 273, 273, + 273, 273, 273, 273, 273, 273, 273, 273, 273, 273, 273, 273, 273, 273, + 273, 273, 81, 81, 81, 273, 273, 273, 273, 273, 273, 273, 273, 81, 273, + 273, 273, 273, 273, 273, 273, 273, 273, 81, 273, 81, 81, 81, 81, 274, 81, + 81, 81, 81, 272, 272, 272, 275, 275, 275, 81, 275, 81, 272, 272, 272, + 272, 272, 272, 272, 272, 81, 81, 81, 81, 81, 81, 276, 276, 276, 276, 276, + 276, 276, 276, 276, 276, 81, 81, 272, 272, 277, 81, 81, 81, 81, 278, 278, + 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, + 279, 278, 278, 279, 279, 279, 279, 280, 280, 281, 81, 81, 81, 81, 282, + 278, 278, 278, 278, 278, 278, 283, 279, 284, 284, 284, 284, 279, 279, + 279, 285, 286, 286, 286, 286, 286, 286, 286, 286, 286, 286, 287, 287, 81, + 81, 81, 81, 81, 288, 288, 81, 288, 81, 288, 288, 288, 288, 288, 81, 288, + 288, 288, 288, 288, 288, 288, 288, 288, 288, 288, 288, 288, 288, 288, + 288, 81, 288, 81, 288, 288, 289, 288, 288, 289, 289, 289, 289, 290, 290, + 291, 289, 289, 288, 81, 81, 288, 288, 288, 288, 288, 81, 292, 81, 293, + 293, 293, 293, 289, 289, 81, 81, 294, 294, 294, 294, 294, 294, 294, 294, + 294, 294, 81, 81, 288, 288, 288, 288, 295, 296, 296, 296, 297, 298, 297, + 297, 299, 297, 297, 300, 299, 301, 301, 301, 301, 301, 299, 302, 301, + 302, 302, 302, 303, 303, 302, 302, 302, 302, 302, 302, 304, 304, 304, + 304, 304, 304, 304, 304, 304, 304, 305, 305, 305, 305, 305, 305, 305, + 305, 305, 305, 306, 303, 302, 303, 302, 307, 308, 309, 308, 309, 310, + 310, 295, 295, 295, 295, 295, 295, 295, 295, 81, 295, 295, 295, 295, 295, + 295, 295, 295, 295, 295, 295, 295, 81, 81, 81, 81, 311, 312, 313, 314, + 313, 313, 313, 313, 313, 312, 312, 312, 312, 313, 315, 312, 313, 316, + 316, 317, 300, 316, 316, 295, 295, 295, 295, 295, 313, 313, 313, 313, + 313, 313, 313, 313, 313, 313, 313, 81, 313, 313, 313, 313, 313, 313, 313, + 313, 313, 313, 313, 313, 81, 306, 306, 302, 302, 302, 302, 302, 302, 303, + 302, 302, 302, 302, 302, 302, 81, 302, 302, 297, 297, 300, 297, 298, 318, + 318, 318, 318, 299, 299, 81, 81, 81, 81, 81, 319, 319, 319, 319, 319, + 319, 319, 319, 319, 319, 319, 320, 320, 321, 321, 321, 321, 320, 321, + 321, 321, 321, 321, 322, 320, 323, 323, 320, 320, 321, 321, 319, 324, + 324, 324, 324, 324, 324, 324, 324, 324, 324, 325, 325, 326, 326, 326, + 326, 319, 319, 319, 319, 319, 319, 320, 320, 321, 321, 319, 319, 319, + 319, 321, 321, 321, 319, 320, 320, 320, 319, 319, 320, 320, 320, 320, + 320, 320, 320, 319, 319, 319, 321, 321, 321, 321, 319, 319, 319, 319, + 319, 321, 320, 320, 321, 321, 320, 320, 320, 320, 320, 320, 327, 319, + 320, 324, 324, 320, 320, 320, 321, 328, 328, 329, 329, 329, 329, 329, + 329, 329, 329, 329, 329, 329, 329, 329, 329, 81, 329, 81, 81, 81, 81, 81, + 329, 81, 81, 330, 330, 330, 330, 330, 330, 330, 330, 330, 330, 330, 331, + 332, 330, 330, 330, 333, 333, 333, 333, 333, 333, 333, 333, 334, 334, + 334, 334, 334, 334, 334, 334, 335, 335, 335, 335, 335, 335, 335, 335, + 336, 336, 336, 336, 336, 336, 336, 336, 336, 81, 336, 336, 336, 336, 81, + 81, 336, 336, 336, 336, 336, 336, 336, 81, 336, 336, 336, 81, 81, 337, + 337, 337, 338, 339, 338, 338, 338, 338, 338, 338, 338, 340, 340, 340, + 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, 340, + 340, 340, 340, 81, 81, 81, 341, 341, 341, 341, 341, 341, 341, 341, 341, + 341, 81, 81, 81, 81, 81, 81, 342, 342, 342, 342, 342, 342, 342, 342, 342, + 342, 342, 342, 342, 342, 81, 81, 343, 343, 343, 343, 343, 343, 81, 81, + 344, 345, 345, 345, 345, 345, 345, 345, 345, 345, 345, 345, 345, 345, + 345, 345, 345, 345, 345, 345, 345, 346, 347, 345, 348, 349, 349, 349, + 349, 349, 349, 349, 349, 349, 349, 349, 349, 349, 349, 349, 349, 349, + 349, 350, 351, 81, 81, 81, 352, 352, 352, 352, 352, 352, 352, 352, 352, + 352, 352, 198, 198, 198, 353, 353, 353, 352, 352, 352, 352, 352, 352, + 352, 352, 81, 81, 81, 81, 81, 81, 81, 354, 354, 354, 354, 354, 354, 354, + 354, 354, 354, 354, 354, 354, 81, 354, 354, 354, 354, 355, 355, 356, 81, + 81, 81, 357, 357, 357, 357, 357, 357, 357, 357, 357, 357, 358, 358, 359, + 198, 198, 81, 360, 360, 360, 360, 360, 360, 360, 360, 360, 360, 361, 361, + 81, 81, 81, 81, 362, 362, 362, 362, 362, 362, 362, 362, 362, 362, 362, + 362, 362, 81, 362, 362, 362, 81, 363, 363, 81, 81, 81, 81, 364, 364, 364, + 364, 364, 364, 364, 364, 364, 364, 364, 364, 365, 365, 366, 365, 365, + 365, 365, 365, 365, 365, 366, 366, 366, 366, 366, 366, 366, 366, 365, + 366, 366, 365, 365, 365, 365, 365, 365, 365, 365, 365, 367, 365, 368, + 368, 369, 370, 368, 371, 368, 372, 364, 373, 81, 81, 374, 374, 374, 374, + 374, 374, 374, 374, 374, 374, 81, 81, 81, 81, 81, 81, 375, 375, 375, 375, + 375, 375, 375, 375, 375, 375, 81, 81, 81, 81, 81, 81, 376, 376, 377, 377, + 378, 379, 380, 376, 381, 381, 376, 382, 382, 382, 383, 81, 384, 384, 384, + 384, 384, 384, 384, 384, 384, 384, 81, 81, 81, 81, 81, 81, 385, 385, 385, + 385, 385, 385, 385, 385, 385, 385, 385, 386, 385, 385, 385, 385, 385, 81, + 81, 81, 81, 81, 81, 81, 385, 385, 385, 385, 385, 382, 382, 385, 385, 387, + 385, 81, 81, 81, 81, 81, 345, 345, 345, 345, 345, 345, 81, 81, 388, 388, + 388, 388, 388, 388, 388, 388, 388, 388, 388, 388, 388, 388, 388, 81, 389, + 389, 389, 390, 390, 390, 390, 389, 389, 390, 390, 390, 81, 81, 81, 81, + 390, 390, 389, 390, 390, 390, 390, 390, 390, 391, 392, 393, 81, 81, 81, + 81, 394, 81, 81, 81, 395, 395, 396, 396, 396, 396, 396, 396, 396, 396, + 396, 396, 397, 397, 397, 397, 397, 397, 397, 397, 397, 397, 397, 397, + 397, 397, 81, 81, 397, 397, 397, 397, 397, 81, 81, 81, 398, 398, 398, + 398, 398, 398, 398, 398, 398, 398, 398, 398, 81, 81, 81, 81, 398, 398, + 81, 81, 81, 81, 81, 81, 399, 399, 399, 399, 399, 399, 399, 399, 399, 399, + 400, 81, 81, 81, 401, 401, 402, 402, 402, 402, 402, 402, 402, 402, 403, + 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, 403, + 404, 405, 406, 406, 407, 81, 81, 408, 408, 409, 409, 409, 409, 409, 409, + 409, 409, 409, 409, 409, 409, 409, 410, 411, 410, 411, 411, 411, 411, + 411, 411, 411, 81, 412, 410, 411, 410, 410, 411, 411, 411, 411, 411, 411, + 411, 411, 410, 410, 410, 410, 410, 410, 411, 411, 413, 413, 413, 413, + 413, 413, 413, 413, 81, 81, 414, 415, 415, 415, 415, 415, 415, 415, 415, + 415, 415, 81, 81, 81, 81, 81, 81, 416, 416, 416, 416, 416, 416, 416, 417, + 416, 416, 416, 416, 416, 416, 81, 81, 96, 96, 96, 96, 96, 156, 156, 156, + 156, 156, 156, 96, 96, 156, 418, 81, 419, 419, 419, 419, 420, 421, 421, + 421, 421, 421, 421, 421, 421, 421, 421, 421, 421, 421, 421, 421, 422, + 420, 419, 419, 419, 419, 419, 420, 419, 420, 420, 420, 420, 420, 419, + 420, 423, 421, 421, 421, 421, 421, 421, 421, 81, 81, 81, 81, 424, 424, + 424, 424, 424, 424, 424, 424, 424, 424, 425, 425, 426, 425, 425, 425, + 425, 427, 427, 427, 427, 427, 427, 427, 427, 427, 427, 428, 429, 428, + 428, 428, 428, 428, 428, 428, 427, 427, 427, 427, 427, 427, 427, 427, + 427, 81, 81, 81, 430, 430, 431, 432, 432, 432, 432, 432, 432, 432, 432, + 432, 432, 432, 432, 432, 432, 431, 430, 430, 430, 430, 431, 431, 430, + 430, 433, 434, 430, 430, 432, 432, 435, 435, 435, 435, 435, 435, 435, + 435, 435, 435, 432, 432, 432, 432, 432, 432, 436, 436, 436, 436, 436, + 436, 436, 436, 436, 436, 436, 436, 436, 436, 437, 438, 439, 439, 438, + 438, 438, 439, 438, 439, 439, 439, 440, 440, 81, 81, 81, 81, 81, 81, 81, + 81, 441, 441, 441, 441, 442, 442, 442, 442, 442, 442, 442, 442, 442, 442, + 442, 442, 443, 443, 443, 443, 443, 443, 443, 443, 444, 444, 444, 444, + 444, 444, 444, 444, 443, 443, 444, 445, 81, 81, 81, 446, 446, 446, 446, + 446, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 81, 81, 81, 442, + 442, 442, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 449, 449, + 449, 449, 449, 449, 449, 449, 449, 449, 449, 449, 449, 449, 450, 450, + 450, 450, 450, 450, 451, 451, 93, 81, 81, 81, 81, 81, 81, 81, 329, 329, + 329, 81, 81, 329, 329, 329, 452, 452, 452, 452, 452, 452, 452, 452, 96, + 96, 96, 331, 453, 156, 156, 156, 156, 156, 96, 96, 156, 156, 156, 156, + 96, 454, 453, 453, 453, 453, 453, 453, 453, 455, 455, 455, 455, 156, 455, + 455, 455, 455, 455, 455, 96, 455, 455, 454, 96, 96, 455, 81, 81, 81, 81, + 81, 56, 56, 56, 56, 56, 56, 79, 79, 79, 79, 79, 93, 59, 59, 59, 59, 59, + 59, 59, 59, 59, 82, 82, 82, 82, 82, 59, 59, 59, 59, 82, 82, 82, 82, 82, + 56, 56, 56, 56, 56, 456, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 59, 59, + 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 82, 96, 96, 156, 96, 96, 96, 96, + 96, 96, 96, 156, 96, 96, 457, 458, 156, 459, 96, 96, 96, 96, 96, 96, 96, + 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 460, 461, 461, + 156, 81, 96, 462, 156, 96, 156, 52, 56, 52, 56, 52, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 52, 56, 79, 79, 79, 79, 79, 79, 79, 79, 78, 78, 78, 78, + 78, 78, 78, 78, 79, 79, 79, 79, 79, 79, 81, 81, 78, 78, 78, 78, 78, 78, + 81, 81, 81, 78, 81, 78, 81, 78, 81, 78, 463, 463, 463, 463, 463, 463, + 463, 463, 79, 79, 79, 79, 79, 81, 79, 79, 78, 78, 78, 78, 463, 80, 79, + 80, 80, 80, 79, 79, 79, 81, 79, 79, 78, 78, 78, 78, 463, 80, 80, 80, 79, + 79, 79, 79, 81, 81, 79, 79, 78, 78, 78, 78, 81, 80, 80, 80, 78, 78, 78, + 78, 78, 80, 80, 80, 81, 81, 79, 79, 79, 81, 79, 79, 78, 78, 78, 78, 463, + 464, 80, 81, 465, 465, 465, 465, 465, 465, 465, 466, 465, 465, 465, 467, + 468, 469, 470, 471, 472, 473, 474, 472, 475, 476, 38, 84, 477, 478, 479, + 42, 477, 478, 479, 42, 38, 38, 480, 84, 481, 481, 481, 482, 483, 484, + 485, 486, 487, 488, 489, 33, 490, 491, 490, 490, 491, 492, 493, 493, 84, + 42, 50, 38, 494, 494, 480, 495, 495, 84, 84, 84, 496, 479, 497, 494, 494, + 494, 84, 84, 84, 84, 84, 84, 84, 84, 498, 84, 495, 84, 379, 84, 379, 379, + 379, 379, 84, 379, 379, 465, 499, 500, 500, 500, 500, 81, 501, 502, 503, + 504, 505, 505, 505, 505, 505, 505, 506, 59, 81, 81, 47, 506, 506, 506, + 506, 506, 507, 507, 498, 479, 497, 508, 506, 47, 47, 47, 47, 506, 506, + 506, 506, 506, 507, 507, 498, 479, 497, 81, 59, 59, 59, 59, 59, 81, 81, + 81, 282, 282, 282, 282, 282, 282, 282, 509, 282, 510, 282, 282, 36, 282, + 282, 282, 282, 282, 282, 282, 282, 282, 509, 282, 282, 282, 282, 509, + 282, 282, 509, 282, 511, 511, 511, 511, 511, 511, 511, 511, 96, 96, 453, + 453, 96, 96, 96, 96, 453, 453, 453, 96, 96, 418, 418, 418, 418, 96, 418, + 418, 418, 453, 453, 96, 156, 96, 453, 453, 156, 156, 156, 156, 96, 81, + 81, 81, 81, 81, 81, 81, 40, 40, 512, 513, 40, 514, 40, 512, 40, 513, 49, + 512, 512, 512, 49, 49, 512, 512, 512, 515, 40, 512, 516, 40, 498, 512, + 512, 512, 512, 512, 40, 40, 40, 514, 514, 40, 512, 40, 85, 40, 512, 40, + 52, 517, 512, 512, 518, 49, 512, 512, 52, 512, 49, 455, 455, 455, 455, + 49, 40, 40, 49, 49, 512, 512, 498, 498, 498, 498, 498, 512, 49, 49, 49, + 49, 40, 498, 40, 40, 56, 318, 519, 519, 519, 520, 51, 521, 519, 519, 519, + 519, 519, 51, 520, 520, 51, 519, 522, 522, 522, 522, 522, 522, 522, 522, + 522, 522, 522, 522, 523, 523, 523, 523, 522, 522, 523, 523, 523, 523, + 523, 523, 523, 523, 523, 52, 56, 523, 523, 523, 523, 51, 40, 40, 81, 81, + 81, 81, 54, 54, 54, 54, 54, 514, 514, 514, 514, 514, 498, 498, 40, 40, + 40, 40, 498, 40, 40, 498, 40, 40, 498, 40, 40, 40, 40, 40, 40, 40, 498, + 40, 40, 40, 40, 40, 40, 40, 40, 40, 44, 44, 40, 40, 40, 40, 40, 40, 40, + 40, 40, 40, 40, 40, 498, 498, 40, 40, 54, 40, 54, 40, 40, 40, 40, 40, 40, + 40, 40, 40, 40, 44, 40, 40, 40, 40, 498, 498, 498, 498, 498, 498, 498, + 498, 498, 498, 498, 498, 54, 498, 54, 54, 498, 498, 498, 54, 54, 498, + 498, 54, 498, 498, 498, 54, 498, 54, 524, 525, 498, 54, 498, 498, 498, + 498, 54, 498, 498, 54, 54, 54, 54, 498, 498, 54, 498, 54, 498, 54, 54, + 54, 54, 54, 54, 498, 54, 498, 498, 498, 498, 498, 54, 54, 54, 54, 498, + 498, 498, 498, 54, 54, 498, 498, 54, 498, 498, 498, 54, 498, 498, 498, + 498, 498, 54, 498, 498, 498, 498, 498, 54, 54, 498, 498, 54, 54, 54, 54, + 498, 498, 54, 54, 498, 498, 54, 54, 498, 498, 498, 498, 498, 54, 498, + 498, 498, 54, 498, 498, 498, 498, 498, 498, 498, 498, 498, 498, 498, 498, + 498, 54, 498, 498, 498, 498, 498, 498, 498, 526, 479, 497, 479, 497, 40, + 40, 40, 40, 40, 40, 514, 40, 40, 40, 40, 40, 40, 40, 527, 527, 40, 40, + 40, 40, 498, 498, 40, 40, 40, 40, 40, 40, 40, 528, 529, 40, 40, 40, 40, + 40, 40, 40, 40, 40, 40, 40, 318, 318, 318, 318, 318, 318, 318, 318, 318, + 318, 318, 318, 318, 40, 498, 40, 40, 40, 40, 40, 40, 40, 40, 318, 40, 40, + 40, 40, 40, 498, 498, 498, 498, 498, 498, 498, 498, 498, 40, 40, 40, 40, + 40, 530, 530, 530, 530, 40, 40, 40, 527, 531, 531, 527, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 81, 40, 40, 40, 81, 81, 81, 81, 81, 51, 51, 51, 51, 51, 51, 51, 51, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, - 530, 530, 530, 530, 530, 530, 530, 530, 530, 530, 530, 530, 530, 530, - 519, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 518, 512, 512, 512, - 512, 512, 512, 512, 512, 512, 512, 512, 512, 40, 40, 40, 40, 512, 512, - 512, 512, 531, 40, 40, 40, 40, 40, 512, 512, 512, 512, 40, 40, 512, 512, - 40, 512, 512, 512, 512, 512, 512, 512, 40, 40, 40, 40, 40, 40, 40, 40, - 512, 512, 40, 40, 512, 54, 40, 40, 40, 40, 512, 512, 40, 40, 512, 54, 40, - 40, 40, 40, 512, 512, 512, 40, 40, 512, 40, 40, 512, 512, 40, 40, 40, 40, - 40, 40, 40, 512, 496, 496, 496, 496, 496, 532, 532, 496, 529, 529, 529, - 529, 40, 512, 512, 40, 40, 512, 40, 40, 40, 40, 512, 512, 40, 40, 40, 40, - 525, 525, 531, 531, 529, 40, 529, 529, 533, 534, 533, 529, 40, 529, 529, - 529, 40, 40, 40, 40, 512, 40, 512, 40, 40, 40, 40, 40, 528, 528, 528, - 528, 528, 528, 528, 528, 528, 528, 528, 528, 40, 40, 40, 40, 512, 512, - 40, 512, 512, 512, 40, 512, 533, 512, 512, 40, 512, 512, 40, 54, 40, 40, - 40, 40, 40, 40, 40, 525, 40, 40, 40, 528, 40, 40, 40, 40, 40, 40, 40, 40, - 40, 40, 512, 512, 40, 528, 40, 40, 40, 40, 40, 40, 40, 40, 528, 528, 317, - 40, 40, 40, 40, 40, 40, 40, 40, 525, 525, 533, 529, 529, 529, 529, 525, - 525, 533, 533, 533, 512, 512, 512, 512, 533, 528, 533, 533, 533, 512, - 533, 525, 512, 512, 512, 533, 533, 512, 512, 533, 512, 512, 533, 533, - 533, 40, 512, 40, 40, 40, 40, 512, 512, 525, 512, 512, 512, 512, 512, - 512, 533, 525, 525, 533, 525, 512, 533, 533, 535, 525, 512, 512, 525, - 533, 533, 529, 529, 529, 529, 529, 528, 40, 40, 529, 529, 536, 536, 534, - 534, 40, 40, 528, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 44, 40, - 40, 40, 40, 40, 40, 528, 40, 528, 40, 40, 40, 40, 528, 528, 528, 40, 537, - 40, 40, 40, 538, 538, 538, 538, 538, 538, 40, 539, 539, 529, 40, 40, 40, - 477, 495, 477, 495, 477, 495, 477, 495, 477, 495, 477, 495, 477, 495, 51, - 51, 519, 519, 519, 519, 519, 519, 519, 519, 519, 519, 519, 519, 40, 528, - 528, 528, 40, 40, 40, 40, 40, 40, 40, 528, 496, 496, 496, 496, 496, 477, - 495, 496, 496, 496, 496, 496, 496, 496, 16, 31, 16, 31, 16, 31, 16, 31, - 477, 495, 540, 540, 540, 540, 540, 540, 540, 540, 496, 496, 496, 477, - 495, 16, 31, 477, 495, 477, 495, 477, 495, 477, 495, 477, 495, 496, 496, - 496, 496, 496, 496, 496, 477, 495, 477, 495, 496, 496, 496, 496, 496, - 496, 496, 496, 477, 495, 496, 496, 40, 40, 40, 528, 528, 40, 40, 40, 496, - 496, 496, 496, 496, 40, 40, 496, 496, 496, 496, 496, 496, 40, 40, 40, - 528, 40, 40, 40, 40, 537, 512, 512, 40, 40, 40, 40, 81, 81, 40, 40, 40, - 40, 40, 40, 40, 40, 81, 81, 40, 81, 40, 40, 40, 40, 40, 40, 541, 541, - 541, 541, 541, 541, 541, 541, 541, 541, 541, 541, 541, 541, 541, 81, 542, - 542, 542, 542, 542, 542, 542, 542, 542, 542, 542, 542, 542, 542, 542, 81, - 52, 56, 52, 52, 52, 56, 56, 52, 56, 52, 56, 52, 56, 52, 52, 52, 52, 56, - 52, 56, 56, 52, 56, 56, 56, 56, 56, 56, 59, 59, 52, 52, 87, 88, 87, 88, - 88, 543, 543, 543, 543, 543, 543, 87, 88, 87, 88, 544, 544, 544, 87, 88, - 81, 81, 81, 81, 81, 545, 546, 546, 546, 547, 545, 546, 329, 329, 329, - 329, 329, 329, 81, 329, 81, 81, 81, 81, 81, 329, 81, 81, 548, 548, 548, - 548, 548, 548, 548, 548, 81, 81, 81, 81, 81, 81, 81, 549, 550, 81, 81, - 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 551, 95, 95, 95, 95, 95, - 95, 95, 95, 552, 552, 42, 50, 42, 50, 552, 552, 552, 42, 50, 552, 42, 50, - 377, 377, 377, 377, 377, 377, 377, 377, 84, 472, 553, 377, 554, 84, 42, - 50, 84, 84, 42, 50, 477, 495, 477, 495, 477, 495, 477, 495, 377, 377, - 377, 377, 375, 60, 377, 377, 84, 377, 377, 84, 84, 84, 84, 84, 555, 555, - 377, 377, 377, 84, 472, 377, 477, 377, 377, 377, 377, 377, 377, 377, 377, - 84, 377, 84, 377, 81, 556, 556, 556, 556, 556, 556, 556, 556, 556, 556, - 81, 556, 556, 556, 556, 556, 556, 556, 556, 556, 81, 81, 81, 81, 556, - 556, 556, 556, 556, 556, 81, 81, 525, 525, 525, 525, 525, 525, 525, 525, - 525, 525, 525, 525, 81, 81, 81, 81, 557, 558, 558, 559, 525, 560, 561, - 562, 526, 527, 526, 527, 526, 527, 526, 527, 526, 527, 525, 525, 526, - 527, 526, 527, 526, 527, 526, 527, 563, 526, 527, 527, 525, 562, 562, - 562, 562, 562, 562, 562, 562, 562, 564, 565, 566, 567, 568, 568, 569, - 570, 570, 570, 570, 571, 525, 525, 562, 562, 562, 560, 572, 559, 525, - 529, 81, 573, 574, 573, 574, 573, 574, 573, 574, 573, 574, 574, 574, 574, - 574, 574, 574, 574, 574, 574, 574, 574, 574, 574, 574, 574, 574, 573, - 574, 574, 574, 574, 574, 574, 574, 573, 574, 573, 574, 573, 574, 574, - 574, 574, 574, 574, 573, 574, 574, 574, 574, 574, 574, 573, 573, 81, 81, - 575, 575, 576, 576, 577, 577, 574, 563, 578, 579, 578, 579, 578, 579, - 578, 579, 578, 579, 579, 579, 579, 579, 579, 579, 579, 579, 579, 579, - 579, 579, 579, 579, 579, 579, 578, 579, 579, 579, 579, 579, 579, 579, - 578, 579, 578, 579, 578, 579, 579, 579, 579, 579, 579, 578, 579, 579, - 579, 579, 579, 579, 578, 578, 579, 579, 579, 579, 580, 581, 582, 582, - 579, 81, 81, 81, 81, 81, 583, 583, 583, 583, 583, 583, 583, 583, 583, - 583, 583, 81, 584, 584, 584, 584, 584, 584, 584, 584, 584, 584, 584, 584, - 584, 584, 584, 584, 584, 584, 584, 584, 584, 584, 81, 585, 585, 586, 586, - 586, 586, 585, 585, 585, 585, 585, 585, 585, 585, 585, 585, 583, 583, - 583, 81, 81, 81, 81, 81, 578, 578, 578, 578, 578, 578, 578, 578, 587, - 587, 587, 587, 587, 587, 587, 587, 587, 587, 587, 587, 587, 588, 588, 81, - 586, 586, 586, 586, 586, 586, 586, 586, 586, 586, 585, 585, 585, 585, - 585, 585, 589, 589, 589, 589, 589, 589, 589, 589, 525, 590, 590, 590, - 590, 590, 590, 590, 590, 590, 590, 590, 590, 590, 590, 590, 587, 587, - 587, 587, 588, 588, 588, 585, 585, 590, 590, 590, 590, 590, 590, 590, - 585, 585, 585, 585, 525, 525, 525, 525, 591, 591, 591, 591, 591, 591, - 591, 591, 591, 591, 591, 591, 591, 591, 591, 81, 585, 585, 585, 585, 585, - 585, 585, 525, 525, 525, 525, 585, 585, 585, 585, 585, 585, 585, 585, - 585, 585, 585, 525, 525, 592, 592, 592, 592, 592, 592, 592, 592, 592, - 592, 592, 592, 592, 592, 593, 593, 593, 593, 593, 593, 593, 593, 593, - 593, 594, 594, 594, 594, 594, 594, 594, 594, 594, 594, 594, 594, 594, - 595, 594, 594, 594, 594, 594, 594, 594, 81, 81, 81, 596, 596, 596, 596, - 596, 596, 596, 596, 596, 596, 596, 596, 596, 596, 596, 81, 597, 597, 597, - 597, 597, 597, 597, 597, 598, 598, 598, 598, 598, 598, 599, 599, 600, - 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 601, 602, 603, - 602, 604, 604, 604, 604, 604, 604, 604, 604, 604, 604, 600, 600, 81, 81, - 81, 81, 90, 93, 90, 93, 90, 93, 605, 95, 97, 97, 97, 606, 95, 95, 95, 95, - 95, 95, 95, 95, 95, 95, 606, 607, 90, 93, 90, 93, 454, 454, 95, 95, 608, - 608, 608, 608, 608, 608, 608, 608, 608, 608, 608, 608, 608, 608, 609, - 609, 609, 609, 609, 609, 609, 609, 609, 609, 610, 610, 611, 612, 612, - 612, 612, 612, 62, 62, 62, 62, 62, 62, 62, 60, 60, 60, 60, 60, 60, 60, - 60, 60, 62, 62, 52, 56, 52, 56, 52, 56, 56, 56, 52, 56, 52, 56, 52, 56, - 59, 56, 56, 56, 56, 56, 56, 56, 56, 52, 56, 52, 56, 52, 52, 56, 60, 613, - 613, 52, 56, 52, 56, 57, 52, 56, 52, 56, 56, 56, 52, 56, 52, 56, 52, 52, - 52, 52, 52, 56, 52, 52, 52, 52, 52, 56, 52, 56, 52, 56, 81, 81, 81, 81, - 81, 81, 81, 81, 81, 81, 81, 81, 81, 57, 59, 59, 56, 57, 57, 57, 57, 57, - 614, 614, 615, 614, 614, 614, 616, 614, 614, 614, 614, 615, 614, 614, - 614, 614, 614, 614, 614, 614, 614, 614, 614, 614, 614, 614, 614, 617, - 617, 615, 615, 617, 618, 618, 618, 618, 81, 81, 81, 81, 619, 619, 619, - 619, 619, 619, 317, 317, 507, 516, 81, 81, 81, 81, 81, 81, 620, 620, 620, - 620, 620, 620, 620, 620, 620, 620, 620, 620, 621, 621, 622, 622, 623, - 623, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, - 624, 624, 624, 624, 624, 623, 623, 623, 623, 623, 623, 623, 623, 623, - 623, 623, 623, 623, 623, 623, 623, 625, 626, 81, 81, 81, 81, 81, 81, 81, - 81, 627, 627, 628, 628, 628, 628, 628, 628, 628, 628, 628, 628, 81, 81, - 81, 81, 81, 81, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 195, - 195, 195, 195, 195, 195, 201, 201, 201, 195, 629, 195, 195, 193, 630, - 630, 630, 630, 630, 630, 630, 630, 630, 630, 631, 631, 631, 631, 631, - 631, 631, 631, 631, 631, 631, 631, 631, 631, 631, 631, 631, 631, 631, - 631, 632, 632, 632, 632, 632, 633, 633, 633, 199, 634, 635, 635, 635, - 635, 635, 635, 635, 635, 635, 635, 635, 635, 635, 635, 635, 636, 636, - 636, 636, 636, 636, 636, 636, 636, 636, 636, 637, 638, 81, 81, 81, 81, - 81, 81, 81, 81, 81, 81, 81, 639, 332, 332, 332, 332, 332, 81, 81, 81, - 640, 640, 640, 641, 642, 642, 642, 642, 642, 642, 642, 642, 642, 642, - 642, 642, 642, 642, 642, 643, 641, 641, 640, 640, 640, 640, 641, 641, - 640, 641, 641, 641, 644, 645, 645, 645, 645, 645, 645, 646, 646, 646, - 645, 645, 645, 645, 81, 61, 647, 647, 647, 647, 647, 647, 647, 647, 647, - 647, 81, 81, 81, 81, 645, 645, 318, 318, 318, 318, 318, 320, 648, 318, - 323, 323, 318, 318, 318, 318, 318, 81, 649, 649, 649, 649, 649, 649, 649, - 649, 649, 650, 650, 650, 650, 650, 650, 651, 651, 650, 650, 651, 651, - 650, 650, 81, 649, 649, 649, 650, 649, 649, 649, 649, 649, 649, 649, 649, - 650, 651, 81, 81, 652, 652, 652, 652, 652, 652, 652, 652, 652, 652, 81, - 81, 653, 654, 654, 654, 648, 318, 318, 318, 318, 318, 318, 327, 327, 327, - 318, 319, 320, 319, 318, 318, 655, 655, 655, 655, 655, 655, 655, 655, - 656, 655, 656, 656, 657, 655, 655, 656, 656, 655, 655, 655, 655, 655, - 656, 656, 655, 656, 655, 81, 81, 81, 81, 81, 81, 81, 81, 655, 655, 658, - 659, 659, 660, 660, 660, 660, 660, 660, 660, 660, 660, 660, 660, 661, - 662, 662, 661, 661, 663, 663, 660, 664, 664, 661, 665, 81, 81, 335, 335, - 335, 335, 335, 335, 81, 56, 56, 56, 613, 59, 59, 59, 59, 56, 56, 56, 56, - 56, 79, 81, 81, 342, 342, 342, 342, 342, 342, 342, 342, 660, 660, 660, - 661, 661, 662, 661, 661, 662, 661, 661, 663, 661, 665, 81, 81, 666, 666, - 666, 666, 666, 666, 666, 666, 666, 666, 81, 81, 81, 81, 81, 81, 667, 668, - 668, 668, 668, 668, 668, 668, 668, 668, 668, 668, 668, 668, 668, 668, - 668, 668, 668, 668, 667, 668, 668, 668, 668, 668, 668, 668, 81, 81, 81, - 81, 333, 333, 333, 333, 333, 333, 333, 81, 81, 81, 81, 334, 334, 334, - 334, 334, 334, 334, 334, 334, 81, 81, 81, 81, 669, 669, 669, 669, 669, - 669, 669, 669, 670, 670, 670, 670, 670, 670, 670, 670, 592, 592, 593, - 593, 593, 593, 593, 593, 56, 56, 56, 56, 56, 56, 56, 81, 81, 81, 81, 101, - 101, 101, 101, 101, 81, 81, 81, 81, 81, 129, 671, 129, 129, 672, 129, + 532, 532, 532, 532, 532, 532, 532, 532, 532, 532, 532, 532, 532, 532, + 521, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 520, 514, 514, 514, + 514, 514, 514, 514, 514, 514, 514, 514, 514, 40, 40, 40, 40, 514, 514, + 514, 514, 533, 40, 40, 40, 40, 40, 514, 514, 514, 514, 40, 40, 514, 514, + 40, 514, 514, 514, 514, 514, 514, 514, 40, 40, 40, 40, 40, 40, 40, 40, + 514, 514, 40, 40, 514, 54, 40, 40, 40, 40, 514, 514, 40, 40, 514, 54, 40, + 40, 40, 40, 514, 514, 514, 40, 40, 514, 40, 40, 514, 514, 40, 40, 40, 40, + 40, 40, 40, 514, 498, 498, 498, 498, 498, 534, 534, 498, 531, 531, 531, + 531, 40, 514, 514, 40, 40, 514, 40, 40, 40, 40, 514, 514, 40, 40, 40, 40, + 527, 527, 533, 533, 531, 40, 531, 531, 535, 536, 535, 531, 40, 531, 531, + 531, 40, 40, 40, 40, 514, 40, 514, 40, 40, 40, 40, 40, 530, 530, 530, + 530, 530, 530, 530, 530, 530, 530, 530, 530, 40, 40, 40, 40, 514, 514, + 40, 514, 514, 514, 40, 514, 535, 514, 514, 40, 514, 514, 40, 54, 40, 40, + 40, 40, 40, 40, 40, 527, 40, 40, 40, 530, 40, 40, 40, 40, 40, 40, 40, 40, + 40, 40, 514, 514, 40, 530, 40, 40, 40, 40, 40, 40, 40, 40, 530, 530, 318, + 40, 40, 40, 40, 40, 40, 40, 40, 527, 527, 535, 531, 531, 531, 531, 527, + 527, 535, 535, 535, 514, 514, 514, 514, 535, 530, 535, 535, 535, 514, + 535, 527, 514, 514, 514, 535, 535, 514, 514, 535, 514, 514, 535, 535, + 535, 40, 514, 40, 40, 40, 40, 514, 514, 527, 514, 514, 514, 514, 514, + 514, 535, 527, 527, 535, 527, 514, 535, 535, 537, 527, 514, 514, 527, + 535, 535, 531, 531, 531, 531, 531, 530, 40, 40, 531, 531, 538, 538, 536, + 536, 40, 40, 530, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 44, 40, + 40, 40, 40, 40, 40, 530, 40, 530, 40, 40, 40, 40, 530, 530, 530, 40, 539, + 40, 40, 40, 540, 540, 540, 540, 540, 540, 40, 541, 541, 531, 40, 40, 40, + 479, 497, 479, 497, 479, 497, 479, 497, 479, 497, 479, 497, 479, 497, 51, + 51, 521, 521, 521, 521, 521, 521, 521, 521, 521, 521, 521, 521, 40, 530, + 530, 530, 40, 40, 40, 40, 40, 40, 40, 530, 498, 498, 498, 498, 498, 479, + 497, 498, 498, 498, 498, 498, 498, 498, 16, 31, 16, 31, 16, 31, 16, 31, + 479, 497, 542, 542, 542, 542, 542, 542, 542, 542, 498, 498, 498, 479, + 497, 16, 31, 479, 497, 479, 497, 479, 497, 479, 497, 479, 497, 498, 498, + 498, 498, 498, 498, 498, 479, 497, 479, 497, 498, 498, 498, 498, 498, + 498, 498, 498, 479, 497, 498, 498, 40, 40, 40, 530, 530, 40, 40, 40, 498, + 498, 498, 498, 498, 40, 40, 498, 498, 498, 498, 498, 498, 40, 40, 40, + 530, 40, 40, 40, 40, 539, 514, 514, 40, 40, 40, 40, 81, 81, 40, 40, 40, + 40, 40, 40, 40, 40, 81, 81, 543, 543, 543, 543, 543, 543, 543, 543, 543, + 543, 543, 543, 543, 543, 543, 81, 544, 544, 544, 544, 544, 544, 544, 544, + 544, 544, 544, 544, 544, 544, 544, 81, 52, 56, 52, 52, 52, 56, 56, 52, + 56, 52, 56, 52, 56, 52, 52, 52, 52, 56, 52, 56, 56, 52, 56, 56, 56, 56, + 56, 56, 59, 59, 52, 52, 87, 88, 87, 88, 88, 545, 545, 545, 545, 545, 545, + 87, 88, 87, 88, 546, 546, 546, 87, 88, 81, 81, 81, 81, 81, 547, 548, 548, + 548, 549, 547, 548, 330, 330, 330, 330, 330, 330, 81, 330, 81, 81, 81, + 81, 81, 330, 81, 81, 550, 550, 550, 550, 550, 550, 550, 550, 81, 81, 81, + 81, 81, 81, 81, 551, 552, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, + 81, 81, 553, 95, 95, 95, 95, 95, 95, 95, 95, 554, 554, 42, 50, 42, 50, + 554, 554, 554, 42, 50, 554, 42, 50, 379, 379, 379, 379, 379, 379, 379, + 379, 84, 474, 555, 379, 556, 84, 42, 50, 84, 84, 42, 50, 479, 497, 479, + 497, 479, 497, 479, 497, 379, 379, 379, 379, 377, 60, 379, 379, 84, 379, + 379, 84, 84, 84, 84, 84, 557, 557, 379, 379, 379, 84, 474, 379, 479, 379, + 379, 379, 379, 379, 379, 379, 379, 84, 379, 84, 379, 379, 558, 558, 558, + 558, 558, 558, 558, 558, 558, 558, 81, 558, 558, 558, 558, 558, 558, 558, + 558, 558, 81, 81, 81, 81, 558, 558, 558, 558, 558, 558, 81, 81, 527, 527, + 527, 527, 527, 527, 527, 527, 527, 527, 527, 527, 81, 81, 81, 81, 559, + 560, 560, 561, 527, 562, 563, 564, 528, 529, 528, 529, 528, 529, 528, + 529, 528, 529, 527, 527, 528, 529, 528, 529, 528, 529, 528, 529, 565, + 528, 529, 529, 527, 564, 564, 564, 564, 564, 564, 564, 564, 564, 566, + 567, 568, 569, 570, 570, 571, 572, 572, 572, 572, 573, 527, 527, 564, + 564, 564, 562, 574, 561, 527, 531, 81, 575, 576, 575, 576, 575, 576, 575, + 576, 575, 576, 576, 576, 576, 576, 576, 576, 576, 576, 576, 576, 576, + 576, 576, 576, 576, 576, 575, 576, 576, 576, 576, 576, 576, 576, 575, + 576, 575, 576, 575, 576, 576, 576, 576, 576, 576, 575, 576, 576, 576, + 576, 576, 576, 575, 575, 81, 81, 577, 577, 578, 578, 579, 579, 576, 565, + 580, 581, 580, 581, 580, 581, 580, 581, 580, 581, 581, 581, 581, 581, + 581, 581, 581, 581, 581, 581, 581, 581, 581, 581, 581, 581, 580, 581, + 581, 581, 581, 581, 581, 581, 580, 581, 580, 581, 580, 581, 581, 581, + 581, 581, 581, 580, 581, 581, 581, 581, 581, 581, 580, 580, 581, 581, + 581, 581, 582, 583, 584, 584, 581, 81, 81, 81, 81, 81, 585, 585, 585, + 585, 585, 585, 585, 585, 585, 585, 585, 81, 586, 586, 586, 586, 586, 586, + 586, 586, 586, 586, 586, 586, 586, 586, 586, 586, 586, 586, 586, 586, + 586, 586, 81, 587, 587, 588, 588, 588, 588, 587, 587, 587, 587, 587, 587, + 587, 587, 587, 587, 585, 585, 585, 81, 81, 81, 81, 81, 580, 580, 580, + 580, 580, 580, 580, 580, 589, 589, 589, 589, 589, 589, 589, 589, 589, + 589, 589, 589, 589, 590, 590, 81, 588, 588, 588, 588, 588, 588, 588, 588, + 588, 588, 587, 587, 587, 587, 587, 587, 591, 591, 591, 591, 591, 591, + 591, 591, 527, 592, 592, 592, 592, 592, 592, 592, 592, 592, 592, 592, + 592, 592, 592, 592, 589, 589, 589, 589, 590, 590, 590, 587, 587, 592, + 592, 592, 592, 592, 592, 592, 587, 587, 587, 587, 527, 527, 527, 527, + 593, 593, 593, 593, 593, 593, 593, 593, 593, 593, 593, 593, 593, 593, + 593, 81, 587, 587, 587, 587, 587, 587, 587, 527, 527, 527, 527, 587, 587, + 587, 587, 587, 587, 587, 587, 587, 587, 587, 527, 527, 594, 594, 594, + 594, 594, 594, 594, 594, 594, 594, 594, 594, 594, 594, 595, 595, 595, + 595, 595, 595, 595, 595, 595, 595, 596, 596, 596, 596, 596, 596, 596, + 596, 596, 596, 596, 596, 596, 597, 596, 596, 596, 596, 596, 596, 596, 81, + 81, 81, 598, 598, 598, 598, 598, 598, 598, 598, 598, 598, 598, 598, 598, + 598, 598, 81, 599, 599, 599, 599, 599, 599, 599, 599, 600, 600, 600, 600, + 600, 600, 601, 601, 602, 602, 602, 602, 602, 602, 602, 602, 602, 602, + 602, 602, 603, 604, 605, 604, 606, 606, 606, 606, 606, 606, 606, 606, + 606, 606, 602, 602, 81, 81, 81, 81, 90, 93, 90, 93, 90, 93, 607, 95, 97, + 97, 97, 608, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 608, 609, 90, 93, + 90, 93, 456, 456, 95, 95, 610, 610, 610, 610, 610, 610, 610, 610, 610, + 610, 610, 610, 610, 610, 611, 611, 611, 611, 611, 611, 611, 611, 611, + 611, 612, 612, 613, 614, 614, 614, 614, 614, 62, 62, 62, 62, 62, 62, 62, + 60, 60, 60, 60, 60, 60, 60, 60, 60, 62, 62, 52, 56, 52, 56, 52, 56, 56, + 56, 52, 56, 52, 56, 52, 56, 59, 56, 56, 56, 56, 56, 56, 56, 56, 52, 56, + 52, 56, 52, 52, 56, 60, 615, 615, 52, 56, 52, 56, 57, 52, 56, 52, 56, 56, + 56, 52, 56, 52, 56, 52, 52, 52, 52, 52, 56, 52, 52, 52, 52, 52, 56, 52, + 56, 81, 81, 52, 56, 52, 52, 52, 81, 81, 81, 81, 81, 81, 81, 81, 57, 59, + 59, 56, 57, 57, 57, 57, 57, 616, 616, 617, 616, 616, 616, 618, 616, 616, + 616, 616, 617, 616, 616, 616, 616, 616, 616, 616, 616, 616, 616, 616, + 616, 616, 616, 616, 619, 619, 617, 617, 619, 620, 620, 620, 620, 81, 81, + 81, 81, 621, 621, 621, 621, 621, 621, 318, 318, 509, 518, 81, 81, 81, 81, + 81, 81, 622, 622, 622, 622, 622, 622, 622, 622, 622, 622, 622, 622, 623, + 623, 624, 624, 625, 625, 626, 626, 626, 626, 626, 626, 626, 626, 626, + 626, 626, 626, 626, 626, 626, 626, 626, 626, 625, 625, 625, 625, 625, + 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 627, 628, 81, 81, + 81, 81, 81, 81, 81, 81, 629, 629, 630, 630, 630, 630, 630, 630, 630, 630, + 630, 630, 81, 81, 81, 81, 81, 81, 631, 631, 631, 631, 631, 631, 631, 631, + 631, 631, 195, 195, 195, 195, 195, 195, 200, 200, 200, 195, 632, 195, + 195, 193, 633, 633, 633, 633, 633, 633, 633, 633, 633, 633, 634, 634, + 634, 634, 634, 634, 634, 634, 634, 634, 634, 634, 634, 634, 634, 634, + 634, 634, 634, 634, 635, 635, 635, 635, 635, 636, 636, 636, 198, 637, + 638, 638, 638, 638, 638, 638, 638, 638, 638, 638, 638, 638, 638, 638, + 638, 639, 639, 639, 639, 639, 639, 639, 639, 639, 639, 639, 640, 641, 81, + 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 642, 333, 333, 333, 333, 333, 81, + 81, 81, 643, 643, 643, 644, 645, 645, 645, 645, 645, 645, 645, 645, 645, + 645, 645, 645, 645, 645, 645, 646, 644, 644, 643, 643, 643, 643, 644, + 644, 643, 643, 644, 644, 647, 648, 648, 648, 648, 648, 648, 649, 649, + 649, 648, 648, 648, 648, 81, 61, 650, 650, 650, 650, 650, 650, 650, 650, + 650, 650, 81, 81, 81, 81, 648, 648, 319, 319, 319, 319, 319, 321, 651, + 319, 324, 324, 319, 319, 319, 319, 319, 81, 652, 652, 652, 652, 652, 652, + 652, 652, 652, 653, 653, 653, 653, 653, 653, 654, 654, 653, 653, 654, + 654, 653, 653, 81, 652, 652, 652, 653, 652, 652, 652, 652, 652, 652, 652, + 652, 653, 654, 81, 81, 655, 655, 655, 655, 655, 655, 655, 655, 655, 655, + 81, 81, 656, 657, 657, 657, 651, 319, 319, 319, 319, 319, 319, 328, 328, + 328, 319, 320, 321, 320, 319, 319, 658, 658, 658, 658, 658, 658, 658, + 658, 659, 658, 659, 659, 660, 658, 658, 659, 659, 658, 658, 658, 658, + 658, 659, 659, 658, 659, 658, 81, 81, 81, 81, 81, 81, 81, 81, 658, 658, + 661, 662, 662, 663, 663, 663, 663, 663, 663, 663, 663, 663, 663, 663, + 664, 665, 665, 664, 664, 666, 666, 663, 667, 667, 664, 668, 81, 81, 336, + 336, 336, 336, 336, 336, 81, 56, 56, 56, 615, 59, 59, 59, 59, 56, 56, 56, + 56, 56, 79, 56, 56, 343, 343, 343, 343, 343, 343, 343, 343, 663, 663, + 663, 664, 664, 665, 664, 664, 665, 664, 664, 666, 664, 668, 81, 81, 669, + 669, 669, 669, 669, 669, 669, 669, 669, 669, 81, 81, 81, 81, 81, 81, 670, + 671, 671, 671, 671, 671, 671, 671, 671, 671, 671, 671, 671, 671, 671, + 671, 671, 671, 671, 671, 670, 671, 671, 671, 671, 671, 671, 671, 81, 81, + 81, 81, 334, 334, 334, 334, 334, 334, 334, 81, 81, 81, 81, 335, 335, 335, + 335, 335, 335, 335, 335, 335, 81, 81, 81, 81, 672, 672, 672, 672, 672, + 672, 672, 672, 673, 673, 673, 673, 673, 673, 673, 673, 594, 594, 595, + 595, 595, 595, 595, 595, 56, 56, 56, 56, 56, 56, 56, 81, 81, 81, 81, 101, + 101, 101, 101, 101, 81, 81, 81, 81, 81, 129, 674, 129, 129, 675, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 81, 129, 129, 129, 129, 129, 81, 129, 81, 129, 129, 81, 129, 129, 81, 129, 129, 146, - 146, 673, 673, 673, 673, 673, 673, 673, 673, 673, 673, 673, 673, 673, - 673, 673, 673, 81, 81, 81, 81, 81, 81, 81, 81, 81, 146, 146, 146, 146, - 146, 146, 146, 146, 146, 146, 146, 495, 477, 81, 81, 146, 146, 146, 146, - 146, 146, 146, 146, 146, 146, 135, 138, 81, 81, 674, 674, 674, 674, 674, - 674, 674, 674, 675, 558, 558, 675, 675, 676, 676, 526, 527, 677, 81, 81, + 146, 676, 676, 676, 676, 676, 676, 676, 676, 676, 676, 676, 676, 676, + 676, 676, 676, 81, 81, 81, 81, 81, 81, 81, 81, 81, 146, 146, 146, 146, + 146, 146, 146, 146, 146, 146, 146, 497, 479, 81, 81, 146, 146, 146, 146, + 146, 146, 146, 146, 146, 146, 135, 138, 81, 81, 677, 677, 677, 677, 677, + 677, 677, 677, 678, 560, 560, 678, 678, 679, 679, 528, 529, 680, 81, 81, 81, 81, 81, 81, 96, 96, 96, 96, 96, 96, 96, 156, 156, 156, 156, 156, 156, - 156, 95, 95, 559, 569, 569, 678, 678, 526, 527, 526, 527, 526, 527, 526, - 527, 526, 527, 526, 527, 526, 527, 526, 527, 559, 559, 526, 527, 559, - 559, 559, 559, 678, 678, 678, 679, 559, 679, 81, 580, 680, 676, 676, 569, - 526, 527, 526, 527, 526, 527, 681, 559, 559, 682, 683, 684, 684, 684, 81, - 559, 685, 686, 559, 81, 81, 81, 81, 146, 146, 146, 146, 146, 81, 81, 497, - 81, 687, 688, 689, 690, 691, 688, 688, 692, 693, 688, 694, 695, 696, 695, - 697, 698, 698, 698, 698, 698, 698, 698, 698, 698, 698, 699, 700, 701, - 701, 701, 687, 688, 702, 702, 702, 702, 702, 702, 702, 702, 702, 702, - 702, 702, 702, 702, 702, 702, 702, 702, 692, 688, 693, 703, 704, 703, - 705, 705, 705, 705, 705, 705, 705, 705, 705, 705, 705, 705, 705, 705, - 705, 705, 705, 705, 692, 701, 693, 701, 692, 693, 706, 707, 708, 706, - 709, 710, 711, 711, 711, 711, 711, 711, 711, 711, 711, 712, 710, 710, - 710, 710, 710, 710, 710, 710, 710, 710, 710, 710, 710, 710, 710, 710, - 710, 710, 710, 710, 710, 713, 713, 714, 714, 714, 714, 714, 714, 714, - 714, 714, 714, 714, 714, 714, 714, 714, 81, 81, 81, 714, 714, 714, 714, - 714, 714, 81, 81, 714, 714, 714, 81, 81, 81, 715, 690, 701, 703, 716, - 690, 690, 81, 717, 718, 718, 718, 718, 717, 717, 81, 81, 719, 719, 719, - 720, 512, 81, 81, 721, 721, 721, 721, 721, 721, 721, 721, 721, 721, 721, - 721, 81, 721, 721, 721, 721, 721, 721, 721, 721, 721, 721, 81, 721, 721, - 721, 81, 721, 721, 81, 721, 721, 721, 721, 721, 721, 721, 81, 81, 721, - 721, 721, 81, 81, 81, 81, 81, 199, 377, 199, 81, 81, 81, 81, 619, 619, - 619, 619, 619, 619, 619, 619, 619, 619, 619, 619, 619, 81, 81, 81, 317, - 722, 722, 722, 722, 722, 722, 722, 722, 722, 722, 722, 722, 722, 723, - 723, 723, 723, 724, 724, 724, 724, 724, 724, 724, 724, 724, 724, 724, - 724, 724, 724, 724, 724, 724, 723, 723, 724, 725, 725, 81, 40, 40, 40, - 40, 81, 81, 81, 81, 724, 81, 81, 81, 81, 81, 81, 81, 317, 317, 317, 317, - 317, 156, 81, 81, 726, 726, 726, 726, 726, 726, 726, 726, 726, 726, 726, - 726, 726, 81, 81, 81, 727, 727, 727, 727, 727, 727, 727, 727, 727, 81, - 81, 81, 81, 81, 81, 81, 156, 504, 504, 504, 504, 504, 504, 504, 504, 504, - 504, 504, 504, 504, 504, 504, 504, 504, 504, 504, 81, 81, 81, 81, 728, - 728, 728, 728, 728, 728, 728, 728, 729, 729, 729, 729, 81, 81, 81, 81, - 81, 81, 81, 81, 81, 728, 728, 728, 730, 730, 730, 730, 730, 730, 730, - 730, 730, 731, 730, 730, 730, 730, 730, 730, 730, 730, 731, 81, 81, 81, - 81, 81, 732, 732, 732, 732, 732, 732, 732, 732, 732, 732, 732, 732, 732, - 732, 733, 733, 733, 733, 733, 81, 81, 81, 81, 81, 734, 734, 734, 734, - 734, 734, 734, 734, 734, 734, 734, 734, 734, 734, 81, 735, 736, 736, 736, - 736, 736, 736, 736, 736, 736, 736, 736, 736, 81, 81, 81, 81, 737, 738, - 738, 738, 738, 738, 81, 81, 739, 739, 739, 739, 739, 739, 739, 739, 740, - 740, 740, 740, 740, 740, 740, 740, 741, 741, 741, 741, 741, 741, 741, - 741, 742, 742, 742, 742, 742, 742, 742, 742, 742, 742, 742, 742, 742, - 742, 81, 81, 743, 743, 743, 743, 743, 743, 743, 743, 743, 743, 81, 81, - 81, 81, 81, 81, 744, 744, 744, 744, 744, 744, 744, 744, 744, 744, 744, - 744, 81, 81, 81, 81, 745, 745, 745, 745, 745, 745, 745, 745, 745, 745, - 745, 745, 81, 81, 81, 81, 746, 746, 746, 746, 746, 746, 746, 746, 747, - 747, 747, 747, 747, 747, 747, 747, 747, 747, 747, 747, 81, 81, 81, 81, - 81, 81, 81, 81, 81, 81, 81, 748, 749, 749, 749, 749, 749, 749, 749, 749, - 749, 749, 749, 749, 749, 749, 749, 81, 749, 749, 749, 749, 749, 749, 81, - 81, 750, 750, 750, 750, 750, 750, 81, 81, 750, 81, 750, 750, 750, 750, - 750, 750, 750, 750, 750, 750, 750, 750, 750, 750, 750, 750, 750, 750, - 750, 750, 81, 750, 750, 81, 81, 81, 750, 81, 81, 750, 751, 751, 751, 751, - 751, 751, 751, 751, 751, 751, 751, 751, 751, 751, 81, 752, 753, 753, 753, - 753, 753, 753, 753, 753, 754, 754, 754, 754, 754, 754, 754, 754, 754, - 754, 754, 754, 754, 754, 754, 755, 755, 756, 756, 756, 756, 756, 756, - 756, 757, 757, 757, 757, 757, 757, 757, 757, 757, 757, 757, 757, 757, - 757, 757, 81, 81, 81, 81, 81, 81, 81, 81, 758, 758, 758, 758, 758, 758, - 758, 758, 758, 759, 759, 759, 759, 759, 759, 759, 759, 759, 759, 759, 81, - 759, 759, 81, 81, 81, 81, 81, 760, 760, 760, 760, 760, 761, 761, 761, - 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 762, 762, 762, - 762, 762, 762, 81, 81, 81, 763, 764, 764, 764, 764, 764, 764, 764, 764, - 764, 764, 81, 81, 81, 81, 81, 765, 766, 766, 766, 766, 766, 766, 766, - 766, 767, 767, 767, 767, 767, 767, 767, 767, 81, 81, 81, 81, 768, 768, - 767, 767, 768, 768, 768, 768, 768, 768, 768, 768, 81, 81, 768, 768, 768, - 768, 768, 768, 769, 770, 770, 770, 81, 770, 770, 81, 81, 81, 81, 81, 770, - 771, 770, 772, 769, 769, 769, 769, 81, 769, 769, 769, 81, 769, 769, 769, - 769, 769, 769, 769, 769, 769, 769, 769, 769, 769, 769, 769, 769, 769, - 769, 769, 769, 769, 81, 81, 772, 773, 771, 81, 81, 81, 81, 774, 775, 775, - 775, 775, 775, 775, 775, 775, 775, 81, 81, 81, 81, 81, 81, 81, 776, 776, - 776, 776, 776, 776, 776, 776, 777, 81, 81, 81, 81, 81, 81, 81, 778, 778, - 778, 778, 778, 778, 778, 778, 778, 778, 778, 778, 778, 779, 779, 780, - 781, 781, 781, 781, 781, 781, 781, 781, 781, 781, 781, 781, 781, 782, - 782, 782, 783, 783, 783, 783, 783, 783, 783, 783, 784, 783, 783, 783, - 783, 783, 783, 783, 783, 783, 783, 783, 783, 785, 786, 81, 81, 81, 81, - 787, 787, 787, 787, 787, 788, 788, 788, 788, 788, 788, 789, 81, 790, 790, - 790, 790, 790, 790, 790, 790, 790, 790, 790, 790, 790, 790, 81, 81, 81, - 791, 791, 791, 791, 791, 791, 791, 792, 792, 792, 792, 792, 792, 792, - 792, 792, 792, 792, 792, 792, 792, 81, 81, 793, 793, 793, 793, 793, 793, - 793, 793, 794, 794, 794, 794, 794, 794, 794, 794, 794, 794, 794, 81, 81, - 81, 81, 81, 795, 795, 795, 795, 795, 795, 795, 795, 796, 796, 796, 796, - 796, 796, 796, 796, 796, 796, 81, 81, 81, 81, 81, 81, 81, 797, 797, 797, - 797, 81, 81, 81, 81, 798, 798, 798, 798, 798, 798, 798, 799, 799, 799, + 156, 95, 95, 561, 571, 571, 681, 681, 528, 529, 528, 529, 528, 529, 528, + 529, 528, 529, 528, 529, 528, 529, 528, 529, 561, 561, 528, 529, 561, + 561, 561, 561, 681, 681, 681, 682, 561, 682, 81, 582, 683, 679, 679, 571, + 528, 529, 528, 529, 528, 529, 684, 561, 561, 685, 686, 687, 687, 687, 81, + 561, 688, 689, 561, 81, 81, 81, 81, 146, 146, 146, 146, 146, 81, 81, 499, + 81, 690, 691, 692, 693, 694, 691, 691, 695, 696, 691, 697, 698, 699, 698, + 700, 701, 701, 701, 701, 701, 701, 701, 701, 701, 701, 702, 703, 704, + 704, 704, 690, 691, 705, 705, 705, 705, 705, 705, 705, 705, 705, 705, + 705, 705, 705, 705, 705, 705, 705, 705, 695, 691, 696, 706, 707, 706, + 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, 708, + 708, 708, 708, 708, 695, 704, 696, 704, 695, 696, 709, 710, 711, 709, + 712, 713, 714, 714, 714, 714, 714, 714, 714, 714, 714, 715, 713, 713, + 713, 713, 713, 713, 713, 713, 713, 713, 713, 713, 713, 713, 713, 713, + 713, 713, 713, 713, 713, 716, 716, 717, 717, 717, 717, 717, 717, 717, + 717, 717, 717, 717, 717, 717, 717, 717, 81, 81, 81, 717, 717, 717, 717, + 717, 717, 81, 81, 717, 717, 717, 81, 81, 81, 718, 693, 704, 706, 719, + 693, 693, 81, 720, 721, 721, 721, 721, 720, 720, 81, 81, 722, 722, 722, + 723, 514, 81, 81, 724, 724, 724, 724, 724, 724, 724, 724, 724, 724, 724, + 724, 81, 724, 724, 724, 724, 724, 724, 724, 724, 724, 724, 81, 724, 724, + 724, 81, 724, 724, 81, 724, 724, 724, 724, 724, 724, 724, 81, 81, 724, + 724, 724, 81, 81, 81, 81, 81, 198, 379, 198, 81, 81, 81, 81, 621, 621, + 621, 621, 621, 621, 621, 621, 621, 621, 621, 621, 621, 81, 81, 81, 318, + 725, 725, 725, 725, 725, 725, 725, 725, 725, 725, 725, 725, 725, 726, + 726, 726, 726, 727, 727, 727, 727, 727, 727, 727, 727, 727, 727, 727, + 727, 727, 727, 727, 727, 727, 726, 726, 727, 728, 728, 81, 40, 40, 40, + 40, 81, 81, 81, 81, 727, 81, 81, 81, 81, 81, 81, 81, 318, 318, 318, 318, + 318, 156, 81, 81, 729, 729, 729, 729, 729, 729, 729, 729, 729, 729, 729, + 729, 729, 81, 81, 81, 730, 730, 730, 730, 730, 730, 730, 730, 730, 81, + 81, 81, 81, 81, 81, 81, 156, 506, 506, 506, 506, 506, 506, 506, 506, 506, + 506, 506, 506, 506, 506, 506, 506, 506, 506, 506, 81, 81, 81, 81, 731, + 731, 731, 731, 731, 731, 731, 731, 732, 732, 732, 732, 81, 81, 81, 81, + 81, 81, 81, 81, 81, 731, 731, 731, 733, 733, 733, 733, 733, 733, 733, + 733, 733, 734, 733, 733, 733, 733, 733, 733, 733, 733, 734, 81, 81, 81, + 81, 81, 735, 735, 735, 735, 735, 735, 735, 735, 735, 735, 735, 735, 735, + 735, 736, 736, 736, 736, 736, 81, 81, 81, 81, 81, 737, 737, 737, 737, + 737, 737, 737, 737, 737, 737, 737, 737, 737, 737, 81, 738, 739, 739, 739, + 739, 739, 739, 739, 739, 739, 739, 739, 739, 81, 81, 81, 81, 740, 741, + 741, 741, 741, 741, 81, 81, 742, 742, 742, 742, 742, 742, 742, 742, 743, + 743, 743, 743, 743, 743, 743, 743, 744, 744, 744, 744, 744, 744, 744, + 744, 745, 745, 745, 745, 745, 745, 745, 745, 745, 745, 745, 745, 745, + 745, 81, 81, 746, 746, 746, 746, 746, 746, 746, 746, 746, 746, 81, 81, + 81, 81, 81, 81, 747, 747, 747, 747, 747, 747, 747, 747, 747, 747, 747, + 747, 81, 81, 81, 81, 748, 748, 748, 748, 748, 748, 748, 748, 748, 748, + 748, 748, 81, 81, 81, 81, 749, 749, 749, 749, 749, 749, 749, 749, 750, + 750, 750, 750, 750, 750, 750, 750, 750, 750, 750, 750, 81, 81, 81, 81, + 81, 81, 81, 81, 81, 81, 81, 751, 752, 752, 752, 752, 752, 752, 752, 752, + 752, 752, 752, 752, 752, 752, 752, 81, 752, 752, 752, 752, 752, 752, 81, + 81, 753, 753, 753, 753, 753, 753, 81, 81, 753, 81, 753, 753, 753, 753, + 753, 753, 753, 753, 753, 753, 753, 753, 753, 753, 753, 753, 753, 753, + 753, 753, 81, 753, 753, 81, 81, 81, 753, 81, 81, 753, 754, 754, 754, 754, + 754, 754, 754, 754, 754, 754, 754, 754, 754, 754, 81, 755, 756, 756, 756, + 756, 756, 756, 756, 756, 757, 757, 757, 757, 757, 757, 757, 757, 757, + 757, 757, 757, 757, 757, 757, 758, 758, 759, 759, 759, 759, 759, 759, + 759, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, + 760, 760, 81, 81, 81, 81, 81, 81, 81, 81, 761, 761, 761, 761, 761, 761, + 761, 761, 761, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 81, + 762, 762, 81, 81, 81, 81, 81, 763, 763, 763, 763, 763, 764, 764, 764, + 764, 764, 764, 764, 764, 764, 764, 764, 764, 764, 764, 765, 765, 765, + 765, 765, 765, 81, 81, 81, 766, 767, 767, 767, 767, 767, 767, 767, 767, + 767, 767, 81, 81, 81, 81, 81, 768, 769, 769, 769, 769, 769, 769, 769, + 769, 770, 770, 770, 770, 770, 770, 770, 770, 81, 81, 81, 81, 771, 771, + 770, 770, 771, 771, 771, 771, 771, 771, 771, 771, 81, 81, 771, 771, 771, + 771, 771, 771, 772, 773, 773, 773, 81, 773, 773, 81, 81, 81, 81, 81, 773, + 774, 773, 775, 772, 772, 772, 772, 81, 772, 772, 772, 81, 772, 772, 772, + 772, 772, 772, 772, 772, 772, 772, 772, 772, 772, 772, 772, 772, 772, + 772, 772, 772, 772, 81, 81, 775, 776, 774, 81, 81, 81, 81, 777, 778, 778, + 778, 778, 778, 778, 778, 778, 778, 81, 81, 81, 81, 81, 81, 81, 779, 779, + 779, 779, 779, 779, 779, 779, 780, 81, 81, 81, 81, 81, 81, 81, 781, 781, + 781, 781, 781, 781, 781, 781, 781, 781, 781, 781, 781, 782, 782, 783, + 784, 784, 784, 784, 784, 784, 784, 784, 784, 784, 784, 784, 784, 785, + 785, 785, 786, 786, 786, 786, 786, 786, 786, 786, 787, 786, 786, 786, + 786, 786, 786, 786, 786, 786, 786, 786, 786, 788, 789, 81, 81, 81, 81, + 790, 790, 790, 790, 790, 791, 791, 791, 791, 791, 791, 792, 81, 793, 793, + 793, 793, 793, 793, 793, 793, 793, 793, 793, 793, 793, 793, 81, 81, 81, + 794, 794, 794, 794, 794, 794, 794, 795, 795, 795, 795, 795, 795, 795, + 795, 795, 795, 795, 795, 795, 795, 81, 81, 796, 796, 796, 796, 796, 796, + 796, 796, 797, 797, 797, 797, 797, 797, 797, 797, 797, 797, 797, 81, 81, + 81, 81, 81, 798, 798, 798, 798, 798, 798, 798, 798, 799, 799, 799, 799, 799, 799, 799, 799, 799, 799, 81, 81, 81, 81, 81, 81, 81, 800, 800, 800, - 800, 800, 800, 800, 800, 800, 800, 800, 81, 81, 81, 81, 81, 801, 801, - 801, 801, 801, 801, 801, 801, 801, 801, 801, 81, 81, 81, 81, 81, 81, 81, - 802, 802, 802, 802, 802, 802, 803, 803, 803, 803, 803, 803, 803, 803, - 803, 803, 803, 803, 804, 804, 804, 804, 805, 805, 805, 805, 805, 805, - 805, 805, 805, 805, 81, 81, 81, 81, 81, 81, 806, 806, 806, 806, 806, 806, - 806, 806, 806, 806, 806, 806, 806, 806, 806, 81, 807, 807, 807, 807, 807, - 807, 807, 807, 807, 807, 807, 807, 807, 808, 808, 808, 808, 808, 808, - 808, 808, 808, 808, 807, 809, 809, 809, 809, 809, 809, 809, 809, 809, - 809, 809, 809, 809, 809, 810, 810, 811, 811, 811, 810, 811, 810, 810, - 810, 810, 812, 812, 812, 812, 813, 813, 813, 813, 813, 81, 81, 81, 81, - 81, 81, 814, 815, 814, 816, 816, 816, 816, 816, 816, 816, 816, 816, 816, - 816, 816, 816, 815, 815, 815, 815, 815, 815, 815, 815, 815, 815, 815, - 815, 815, 815, 817, 818, 818, 819, 819, 819, 819, 819, 81, 81, 81, 81, - 820, 820, 820, 820, 820, 820, 820, 820, 820, 820, 820, 820, 820, 820, - 820, 820, 820, 820, 820, 820, 821, 821, 821, 821, 821, 821, 821, 821, - 821, 821, 81, 81, 81, 81, 81, 81, 81, 817, 822, 822, 823, 824, 824, 824, - 824, 824, 824, 824, 824, 824, 824, 824, 824, 824, 823, 823, 823, 822, - 822, 822, 822, 823, 823, 825, 826, 827, 827, 828, 829, 829, 829, 829, 81, - 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 828, 81, 81, 830, 830, 830, 830, - 830, 830, 830, 830, 830, 81, 81, 81, 81, 81, 81, 81, 831, 831, 831, 831, - 831, 831, 831, 831, 831, 831, 81, 81, 81, 81, 81, 81, 832, 832, 832, 833, - 833, 833, 833, 833, 833, 833, 833, 833, 833, 833, 833, 833, 833, 833, - 833, 833, 833, 833, 833, 834, 834, 834, 834, 834, 835, 834, 834, 834, - 834, 834, 834, 836, 836, 81, 837, 837, 837, 837, 837, 837, 837, 837, 837, - 837, 838, 838, 838, 838, 833, 835, 835, 81, 839, 839, 839, 839, 839, 839, - 839, 839, 839, 839, 839, 840, 841, 842, 839, 81, 843, 843, 844, 845, 845, - 845, 845, 845, 845, 845, 845, 845, 845, 845, 845, 845, 845, 845, 845, - 844, 844, 844, 843, 843, 843, 843, 843, 843, 843, 843, 843, 844, 846, - 845, 845, 845, 845, 847, 847, 848, 847, 843, 849, 843, 843, 848, 81, 81, - 850, 850, 850, 850, 850, 850, 850, 850, 850, 850, 845, 851, 845, 847, - 847, 847, 81, 852, 852, 852, 852, 852, 852, 852, 852, 852, 852, 852, 852, - 852, 852, 852, 852, 852, 852, 852, 852, 81, 81, 81, 853, 853, 853, 853, - 853, 853, 853, 853, 853, 853, 81, 853, 853, 853, 853, 853, 853, 853, 853, - 853, 854, 854, 854, 855, 855, 855, 854, 854, 855, 856, 857, 855, 858, - 858, 859, 858, 858, 859, 855, 81, 860, 860, 860, 860, 860, 860, 860, 81, - 860, 81, 860, 860, 860, 860, 81, 860, 860, 860, 860, 860, 860, 860, 860, - 860, 860, 860, 860, 860, 860, 860, 81, 860, 860, 861, 81, 81, 81, 81, 81, - 81, 862, 862, 862, 862, 862, 862, 862, 862, 862, 862, 862, 862, 862, 862, - 862, 863, 864, 864, 864, 863, 863, 863, 863, 863, 863, 865, 866, 81, 81, - 81, 81, 81, 867, 867, 867, 867, 867, 867, 867, 867, 867, 867, 81, 81, 81, - 81, 81, 81, 868, 868, 869, 869, 81, 870, 870, 870, 870, 870, 870, 870, - 870, 81, 81, 870, 870, 81, 81, 870, 870, 870, 870, 870, 870, 870, 870, - 870, 870, 870, 870, 870, 870, 81, 870, 870, 870, 870, 870, 870, 870, 81, - 870, 870, 81, 870, 870, 870, 870, 870, 81, 871, 872, 870, 869, 869, 868, - 869, 869, 869, 869, 81, 81, 869, 869, 81, 81, 869, 869, 873, 81, 81, 870, - 81, 81, 81, 81, 81, 81, 869, 81, 81, 81, 81, 81, 870, 870, 870, 870, 870, - 869, 869, 81, 81, 874, 874, 874, 874, 874, 874, 874, 81, 81, 81, 875, - 875, 875, 875, 875, 875, 875, 875, 875, 875, 875, 875, 875, 876, 876, - 876, 877, 877, 877, 877, 877, 877, 877, 877, 876, 876, 878, 877, 877, - 876, 879, 875, 875, 875, 875, 880, 880, 880, 880, 881, 882, 882, 882, - 882, 882, 882, 882, 882, 882, 882, 81, 880, 81, 881, 883, 81, 884, 884, - 884, 884, 884, 884, 884, 884, 885, 885, 885, 886, 886, 886, 886, 886, - 886, 885, 886, 885, 885, 885, 885, 886, 886, 885, 887, 888, 884, 884, - 889, 884, 890, 890, 890, 890, 890, 890, 890, 890, 890, 890, 81, 81, 81, - 81, 81, 81, 891, 891, 891, 891, 891, 891, 891, 891, 891, 891, 891, 891, - 891, 891, 891, 892, 892, 892, 893, 893, 893, 893, 81, 81, 892, 892, 892, - 892, 893, 893, 892, 894, 895, 896, 897, 897, 898, 898, 899, 899, 899, - 897, 897, 897, 897, 897, 897, 897, 897, 897, 897, 897, 897, 897, 897, - 897, 891, 891, 891, 891, 893, 893, 81, 81, 900, 900, 900, 900, 900, 900, - 900, 900, 901, 901, 901, 902, 902, 902, 902, 902, 902, 902, 902, 901, - 901, 902, 901, 903, 902, 904, 904, 905, 900, 81, 81, 81, 906, 906, 906, - 906, 906, 906, 906, 906, 906, 906, 81, 81, 81, 81, 81, 81, 907, 907, 907, - 907, 907, 907, 907, 907, 907, 907, 907, 907, 907, 81, 81, 81, 908, 908, - 908, 908, 908, 908, 908, 908, 908, 908, 908, 909, 910, 909, 910, 910, - 909, 909, 909, 909, 909, 909, 911, 912, 913, 913, 913, 913, 913, 913, - 913, 913, 913, 913, 81, 81, 81, 81, 81, 81, 914, 914, 914, 914, 914, 914, - 914, 914, 914, 914, 914, 81, 81, 915, 915, 915, 916, 916, 915, 915, 915, - 915, 916, 915, 915, 915, 915, 917, 81, 81, 81, 81, 918, 918, 918, 918, - 918, 918, 918, 918, 918, 918, 919, 919, 920, 920, 920, 921, 922, 922, - 922, 922, 922, 922, 922, 922, 922, 922, 922, 922, 923, 923, 923, 924, - 924, 924, 924, 924, 924, 924, 924, 924, 923, 925, 926, 927, 81, 81, 81, - 81, 928, 928, 928, 928, 928, 928, 928, 928, 929, 929, 929, 929, 929, 929, - 929, 929, 930, 930, 930, 930, 930, 930, 930, 930, 930, 930, 931, 931, - 931, 931, 931, 931, 931, 931, 931, 81, 81, 81, 81, 81, 81, 81, 81, 81, - 81, 81, 81, 932, 933, 934, 934, 934, 934, 934, 934, 935, 935, 934, 934, - 933, 933, 933, 933, 933, 933, 933, 933, 933, 933, 933, 933, 933, 933, - 933, 933, 934, 936, 934, 934, 934, 934, 937, 933, 934, 934, 934, 934, - 938, 939, 940, 940, 940, 940, 938, 939, 936, 941, 942, 942, 942, 942, - 942, 942, 943, 943, 942, 942, 942, 941, 941, 941, 941, 941, 941, 941, - 941, 941, 941, 941, 941, 941, 941, 941, 941, 81, 81, 941, 941, 941, 941, - 942, 942, 942, 942, 942, 942, 942, 942, 942, 942, 942, 942, 942, 943, - 942, 944, 945, 945, 945, 941, 946, 946, 946, 945, 945, 81, 81, 81, 81, - 81, 947, 947, 947, 947, 947, 947, 947, 947, 947, 81, 81, 81, 81, 81, 81, - 81, 948, 948, 948, 948, 948, 948, 948, 948, 948, 81, 948, 948, 948, 948, - 948, 948, 948, 948, 948, 948, 948, 948, 948, 949, 950, 950, 950, 950, - 950, 950, 950, 81, 950, 950, 950, 950, 950, 950, 949, 951, 948, 952, 952, - 952, 952, 952, 81, 81, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, - 954, 954, 954, 954, 954, 954, 954, 954, 954, 954, 954, 954, 954, 954, - 954, 954, 954, 954, 954, 81, 81, 81, 955, 956, 957, 957, 957, 957, 957, - 957, 957, 957, 957, 957, 957, 957, 957, 957, 81, 81, 958, 958, 958, 958, - 958, 958, 958, 958, 958, 958, 958, 958, 958, 958, 81, 959, 958, 958, 958, - 958, 958, 958, 958, 959, 958, 958, 959, 958, 958, 81, 960, 960, 960, 960, - 960, 960, 960, 81, 960, 960, 81, 960, 960, 960, 960, 960, 960, 960, 960, - 960, 960, 960, 960, 960, 960, 961, 961, 961, 961, 961, 961, 81, 81, 81, - 961, 81, 961, 961, 81, 961, 961, 961, 962, 961, 963, 963, 960, 961, 964, - 964, 964, 964, 964, 964, 964, 964, 964, 964, 81, 81, 81, 81, 81, 81, 965, - 965, 965, 965, 965, 965, 81, 965, 965, 81, 965, 965, 965, 965, 965, 965, - 965, 965, 965, 965, 965, 965, 965, 965, 965, 965, 966, 966, 966, 966, - 966, 81, 967, 967, 81, 966, 966, 967, 966, 968, 965, 81, 81, 81, 81, 81, - 81, 81, 969, 969, 969, 969, 969, 969, 969, 969, 969, 969, 81, 81, 81, 81, - 81, 81, 970, 970, 970, 970, 970, 970, 970, 970, 970, 970, 970, 971, 971, - 972, 972, 973, 973, 81, 81, 81, 81, 81, 81, 81, 974, 974, 974, 974, 974, - 974, 974, 974, 974, 974, 81, 81, 81, 81, 81, 81, 975, 975, 975, 975, 975, - 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 81, 976, 976, 976, 976, - 976, 81, 81, 81, 974, 974, 974, 974, 81, 81, 81, 81, 977, 977, 977, 977, - 977, 977, 977, 977, 978, 978, 978, 979, 979, 979, 977, 977, 977, 977, - 979, 977, 977, 977, 978, 979, 978, 979, 977, 977, 977, 977, 977, 977, - 977, 978, 979, 979, 977, 977, 977, 977, 977, 977, 977, 977, 977, 977, - 977, 81, 980, 980, 980, 980, 980, 980, 980, 980, 980, 980, 980, 980, 980, - 980, 981, 982, 980, 980, 980, 980, 980, 980, 980, 81, 608, 81, 81, 81, - 81, 81, 81, 81, 983, 983, 983, 983, 983, 983, 983, 983, 983, 983, 983, - 983, 983, 983, 983, 81, 984, 984, 984, 984, 984, 984, 984, 984, 984, 984, - 81, 81, 81, 81, 985, 985, 986, 986, 986, 986, 986, 986, 986, 986, 986, - 986, 986, 986, 986, 986, 81, 81, 987, 987, 987, 987, 987, 988, 81, 81, - 989, 989, 989, 989, 989, 989, 989, 989, 990, 990, 990, 990, 990, 990, - 990, 991, 991, 991, 992, 992, 993, 993, 993, 993, 994, 994, 994, 994, - 991, 993, 81, 81, 995, 995, 995, 995, 995, 995, 995, 995, 995, 995, 81, - 996, 996, 996, 996, 996, 996, 996, 81, 989, 989, 989, 989, 989, 81, 81, - 81, 81, 81, 989, 989, 989, 997, 997, 997, 997, 997, 997, 997, 997, 998, - 998, 998, 998, 998, 998, 998, 998, 999, 999, 999, 999, 999, 999, 999, - 999, 999, 999, 999, 999, 999, 999, 999, 1000, 1000, 1001, 1001, 81, 81, - 81, 81, 81, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, - 1002, 1002, 1002, 81, 81, 81, 1002, 1003, 1003, 1003, 1003, 1003, 1003, - 1003, 1003, 1003, 1003, 1003, 1003, 1003, 1003, 1003, 1003, 1003, 1003, - 1003, 1003, 1003, 1003, 81, 81, 81, 81, 81, 81, 81, 81, 1004, 1004, 1004, - 1004, 1005, 1005, 1005, 1005, 1005, 1005, 1005, 1005, 1005, 1005, 1005, - 1005, 1005, 1006, 1007, 81, 81, 81, 81, 81, 81, 1008, 1008, 1008, 1008, - 1008, 1008, 1008, 1008, 1008, 1008, 81, 81, 81, 81, 81, 81, 1008, 1008, - 1008, 81, 81, 81, 81, 81, 579, 574, 574, 574, 574, 574, 574, 574, 574, - 574, 574, 574, 574, 574, 574, 81, 1009, 1009, 1009, 1009, 1009, 1009, - 1009, 1009, 1009, 1009, 1009, 1009, 81, 81, 81, 81, 1010, 1010, 1010, - 1010, 1010, 1010, 1010, 1010, 1010, 1010, 1010, 81, 81, 81, 81, 81, 1010, - 1010, 1010, 1010, 1010, 81, 81, 81, 1010, 81, 81, 81, 81, 81, 81, 81, - 1010, 1010, 81, 81, 1011, 1012, 1013, 1014, 503, 503, 503, 503, 81, 81, - 81, 81, 317, 317, 317, 317, 317, 317, 81, 81, 317, 317, 317, 317, 317, - 317, 317, 81, 81, 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, 317, - 317, 1015, 1015, 451, 451, 451, 317, 317, 317, 1016, 1015, 1015, 1015, - 1015, 1015, 503, 503, 503, 503, 503, 503, 503, 503, 156, 156, 156, 156, - 156, 156, 156, 156, 317, 317, 96, 96, 96, 96, 96, 156, 156, 317, 317, - 317, 317, 317, 317, 96, 96, 96, 96, 317, 317, 317, 81, 81, 81, 81, 81, - 81, 81, 724, 724, 1017, 1017, 1017, 724, 81, 81, 619, 619, 619, 619, 81, - 81, 81, 81, 619, 81, 81, 81, 81, 81, 81, 81, 510, 510, 510, 510, 510, - 510, 510, 510, 510, 510, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, - 49, 49, 49, 49, 49, 49, 510, 510, 510, 510, 510, 510, 510, 510, 510, 510, - 49, 49, 49, 49, 49, 49, 49, 81, 49, 49, 49, 49, 49, 49, 510, 81, 510, - 510, 81, 81, 510, 81, 81, 510, 510, 81, 81, 510, 510, 510, 510, 81, 510, - 510, 49, 49, 81, 49, 81, 49, 49, 49, 49, 49, 49, 49, 81, 49, 49, 49, 49, - 49, 49, 49, 510, 510, 81, 510, 510, 510, 510, 81, 81, 510, 510, 510, 510, - 510, 510, 510, 510, 81, 510, 510, 510, 510, 510, 510, 510, 81, 49, 49, - 510, 510, 81, 510, 510, 510, 510, 81, 510, 510, 510, 510, 510, 81, 510, - 81, 81, 81, 510, 510, 510, 510, 510, 510, 510, 81, 49, 49, 49, 49, 49, - 49, 49, 49, 49, 49, 49, 49, 81, 81, 510, 1018, 49, 49, 49, 49, 49, 49, - 49, 49, 49, 496, 49, 49, 49, 49, 49, 49, 510, 510, 510, 510, 510, 510, - 510, 510, 510, 1018, 49, 49, 49, 49, 49, 49, 49, 49, 49, 496, 49, 49, - 510, 510, 510, 510, 510, 1018, 49, 49, 49, 49, 49, 49, 49, 49, 49, 496, - 49, 49, 49, 49, 49, 49, 510, 510, 510, 510, 510, 510, 510, 510, 510, - 1018, 49, 496, 49, 49, 49, 49, 49, 49, 49, 49, 510, 49, 81, 81, 1019, - 1019, 1019, 1019, 1019, 1019, 1019, 1019, 1019, 1019, 1020, 1020, 1020, - 1020, 1020, 1020, 1020, 1020, 1021, 1021, 1021, 1021, 1021, 1021, 1021, - 1021, 1021, 1021, 1021, 1021, 1021, 1021, 1021, 1020, 1020, 1020, 1020, - 1021, 1021, 1021, 1021, 1021, 1021, 1021, 1021, 1021, 1021, 1020, 1020, - 1020, 1020, 1020, 1020, 1020, 1020, 1021, 1020, 1020, 1020, 1020, 1020, - 1020, 1021, 1020, 1020, 1022, 1022, 1022, 1022, 1023, 81, 81, 81, 81, 81, - 81, 81, 1021, 1021, 1021, 1021, 1021, 81, 1021, 1021, 1021, 1021, 1021, - 1021, 1021, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 81, 1024, 1024, - 1024, 1024, 1024, 1024, 1024, 1024, 1024, 81, 81, 1024, 1024, 1024, 1024, - 1024, 1024, 1024, 81, 1024, 1024, 81, 1024, 1024, 1024, 1024, 1024, 81, - 81, 81, 81, 81, 1025, 1025, 1025, 1025, 1025, 1025, 1025, 1025, 1025, - 1025, 1025, 1025, 1025, 81, 81, 1026, 1026, 1026, 1026, 1026, 1026, 1026, - 1026, 1026, 1027, 1027, 1027, 1027, 1027, 1027, 1027, 81, 1028, 1028, - 1028, 1028, 1028, 1028, 1028, 1028, 1028, 1028, 1029, 1029, 1029, 1029, - 1029, 1029, 1029, 1029, 1029, 1029, 1029, 1029, 1029, 1029, 1029, 1029, - 1029, 1029, 1030, 1030, 1030, 1030, 1030, 1030, 1031, 81, 81, 81, 81, 81, - 1032, 1032, 1032, 1032, 1032, 1032, 1032, 1032, 1032, 1032, 81, 81, 81, - 81, 1033, 1033, 81, 1034, 1034, 1034, 1034, 1034, 1034, 1034, 1034, 1034, - 1034, 1034, 1034, 1034, 1034, 1034, 1034, 1034, 1034, 1034, 1035, 1034, - 1034, 1034, 1036, 1034, 1034, 1034, 1034, 81, 81, 81, 146, 146, 146, 146, - 81, 146, 146, 146, 81, 146, 146, 81, 146, 81, 81, 146, 81, 146, 146, 146, - 146, 146, 146, 146, 146, 146, 146, 81, 146, 146, 146, 146, 81, 146, 81, - 146, 81, 81, 81, 81, 81, 81, 146, 81, 81, 81, 81, 146, 81, 146, 81, 146, - 81, 146, 146, 146, 81, 146, 81, 146, 81, 146, 81, 146, 81, 146, 146, 146, - 146, 81, 146, 81, 146, 146, 81, 146, 146, 146, 146, 146, 146, 146, 146, - 146, 81, 81, 81, 81, 81, 146, 146, 146, 81, 146, 146, 146, 132, 132, 81, - 81, 81, 81, 81, 81, 529, 529, 529, 529, 525, 529, 529, 529, 529, 529, - 529, 529, 529, 529, 529, 529, 529, 529, 529, 529, 1037, 1037, 1037, 1037, - 1037, 1037, 1037, 1037, 1037, 1037, 1037, 1037, 529, 529, 529, 529, 529, - 529, 529, 1037, 1037, 529, 529, 529, 529, 529, 529, 529, 529, 529, 529, - 529, 529, 529, 529, 525, 529, 529, 529, 529, 529, 529, 1037, 1037, 47, - 47, 47, 519, 519, 1037, 1037, 1037, 530, 530, 530, 530, 530, 530, 317, - 40, 530, 530, 40, 40, 1037, 1037, 1037, 1037, 530, 530, 530, 530, 530, - 530, 1038, 530, 530, 1038, 1038, 1038, 1038, 1038, 1038, 1038, 1038, - 1038, 1038, 530, 530, 530, 530, 530, 530, 530, 530, 530, 530, 1037, 1037, - 1037, 1037, 1037, 1037, 1037, 1037, 1037, 1039, 1039, 1039, 1039, 1039, - 1039, 1039, 1039, 1039, 1039, 1040, 585, 585, 1037, 1037, 1037, 1037, - 1037, 585, 585, 585, 585, 1037, 1037, 1037, 1037, 585, 1037, 1037, 1037, - 1037, 1037, 1037, 1037, 585, 585, 1037, 1037, 1037, 1037, 1037, 1037, - 525, 525, 525, 525, 525, 525, 1037, 1037, 525, 529, 529, 529, 529, 529, - 529, 529, 529, 529, 529, 529, 529, 525, 525, 525, 525, 525, 525, 525, - 525, 525, 529, 525, 525, 525, 525, 525, 525, 529, 525, 525, 525, 525, - 525, 525, 525, 536, 525, 525, 525, 525, 525, 525, 529, 529, 529, 529, - 529, 529, 529, 529, 40, 40, 529, 529, 525, 525, 525, 525, 525, 528, 528, - 525, 525, 525, 525, 525, 528, 525, 525, 525, 525, 525, 536, 536, 536, - 525, 525, 536, 525, 525, 536, 534, 534, 529, 529, 525, 525, 529, 529, - 529, 525, 529, 529, 529, 525, 525, 525, 1041, 1041, 1041, 1041, 1041, - 525, 525, 525, 525, 525, 525, 525, 529, 525, 529, 536, 536, 525, 525, - 536, 536, 536, 536, 536, 536, 536, 536, 536, 536, 536, 525, 525, 525, - 525, 525, 525, 525, 525, 525, 525, 525, 525, 525, 536, 536, 536, 536, - 525, 525, 525, 525, 536, 525, 536, 525, 525, 525, 536, 525, 525, 525, - 525, 536, 536, 536, 525, 536, 536, 536, 528, 525, 528, 525, 528, 525, - 525, 525, 525, 525, 536, 525, 525, 525, 525, 528, 525, 528, 528, 525, - 525, 525, 525, 525, 525, 525, 525, 525, 525, 529, 529, 525, 528, 528, - 528, 528, 528, 528, 528, 525, 525, 525, 525, 525, 525, 525, 525, 528, - 528, 528, 528, 528, 528, 525, 525, 525, 525, 525, 528, 528, 528, 528, - 528, 528, 528, 528, 528, 528, 528, 528, 40, 40, 40, 40, 529, 525, 525, - 525, 525, 529, 529, 529, 529, 529, 534, 534, 529, 529, 529, 529, 536, - 529, 529, 529, 529, 529, 534, 529, 529, 529, 529, 536, 536, 529, 529, - 529, 529, 529, 40, 40, 40, 40, 40, 40, 40, 40, 529, 529, 529, 529, 40, - 40, 529, 525, 525, 525, 525, 525, 525, 525, 525, 525, 525, 536, 536, 536, - 525, 525, 525, 536, 536, 536, 536, 536, 40, 40, 40, 40, 40, 40, 538, 538, - 538, 1042, 1042, 1042, 40, 40, 40, 40, 525, 525, 525, 536, 525, 525, 525, - 525, 525, 525, 525, 525, 536, 536, 536, 525, 536, 525, 525, 525, 525, - 525, 529, 529, 529, 529, 529, 529, 536, 529, 529, 529, 525, 525, 525, - 529, 529, 1037, 1037, 1037, 529, 529, 529, 525, 525, 1037, 1037, 1037, - 529, 529, 529, 529, 525, 525, 525, 525, 525, 525, 1037, 1037, 1037, 1037, - 1037, 1037, 40, 40, 40, 40, 1037, 1037, 1037, 1037, 40, 40, 40, 40, 40, - 529, 529, 529, 529, 1037, 1037, 1037, 1037, 1037, 1037, 1037, 40, 40, - 1037, 1037, 1037, 1037, 1037, 1037, 40, 40, 40, 40, 40, 40, 1037, 1037, - 536, 536, 536, 536, 536, 525, 536, 536, 525, 525, 525, 525, 525, 525, - 536, 525, 536, 536, 525, 525, 525, 536, 536, 1037, 525, 1037, 1037, 525, - 525, 525, 525, 1037, 1037, 1037, 525, 1037, 525, 525, 525, 525, 525, 525, - 525, 1037, 1037, 1037, 1037, 1037, 525, 525, 525, 525, 525, 536, 536, - 525, 536, 536, 1037, 1037, 1037, 1037, 1037, 1037, 525, 536, 536, 536, - 536, 536, 536, 536, 536, 536, 536, 536, 536, 536, 525, 525, 1037, 1037, - 1037, 1037, 1037, 1037, 81, 81, 592, 592, 592, 592, 592, 592, 592, 593, - 592, 592, 592, 592, 592, 593, 593, 593, 592, 593, 593, 593, 593, 593, - 593, 593, 593, 593, 593, 593, 593, 593, 81, 81, 81, 503, 81, 81, 81, 81, - 81, 81, 503, 503, 503, 503, 503, 503, 503, 503, 670, 670, 670, 670, 670, - 670, 81, 81, + 800, 81, 81, 81, 81, 801, 801, 801, 801, 801, 801, 801, 802, 802, 802, + 802, 802, 802, 802, 802, 802, 81, 81, 81, 81, 81, 81, 81, 803, 803, 803, + 803, 803, 803, 803, 803, 803, 803, 803, 81, 81, 81, 81, 81, 804, 804, + 804, 804, 804, 804, 804, 804, 804, 804, 804, 81, 81, 81, 81, 81, 81, 81, + 805, 805, 805, 805, 805, 805, 806, 806, 806, 806, 806, 806, 806, 806, + 806, 806, 806, 806, 807, 807, 807, 807, 808, 808, 808, 808, 808, 808, + 808, 808, 808, 808, 81, 81, 81, 81, 81, 81, 809, 809, 809, 809, 809, 809, + 809, 809, 809, 809, 809, 809, 809, 809, 809, 81, 810, 810, 810, 810, 810, + 810, 810, 810, 810, 810, 810, 810, 810, 811, 811, 811, 811, 811, 811, + 811, 811, 811, 811, 810, 812, 812, 812, 812, 812, 812, 812, 812, 812, + 812, 812, 812, 812, 812, 813, 813, 814, 814, 814, 813, 814, 813, 813, + 813, 813, 815, 815, 815, 815, 816, 816, 816, 816, 816, 81, 81, 81, 81, + 81, 81, 817, 817, 817, 817, 817, 817, 817, 817, 817, 817, 817, 817, 817, + 817, 817, 81, 818, 819, 818, 820, 820, 820, 820, 820, 820, 820, 820, 820, + 820, 820, 820, 820, 819, 819, 819, 819, 819, 819, 819, 819, 819, 819, + 819, 819, 819, 819, 821, 822, 822, 823, 823, 823, 823, 823, 81, 81, 81, + 81, 824, 824, 824, 824, 824, 824, 824, 824, 824, 824, 824, 824, 824, 824, + 824, 824, 824, 824, 824, 824, 825, 825, 825, 825, 825, 825, 825, 825, + 825, 825, 81, 81, 81, 81, 81, 81, 81, 821, 826, 826, 827, 828, 828, 828, + 828, 828, 828, 828, 828, 828, 828, 828, 828, 828, 827, 827, 827, 826, + 826, 826, 826, 827, 827, 829, 830, 831, 831, 832, 833, 833, 833, 833, 81, + 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 832, 81, 81, 834, 834, 834, 834, + 834, 834, 834, 834, 834, 81, 81, 81, 81, 81, 81, 81, 835, 835, 835, 835, + 835, 835, 835, 835, 835, 835, 81, 81, 81, 81, 81, 81, 836, 836, 836, 837, + 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, + 837, 837, 837, 837, 837, 838, 838, 838, 838, 838, 839, 838, 838, 838, + 838, 838, 838, 840, 840, 81, 841, 841, 841, 841, 841, 841, 841, 841, 841, + 841, 842, 842, 842, 842, 837, 839, 839, 81, 843, 843, 843, 843, 843, 843, + 843, 843, 843, 843, 843, 844, 845, 846, 843, 81, 847, 847, 848, 849, 849, + 849, 849, 849, 849, 849, 849, 849, 849, 849, 849, 849, 849, 849, 849, + 848, 848, 848, 847, 847, 847, 847, 847, 847, 847, 847, 847, 848, 850, + 849, 849, 849, 849, 851, 851, 852, 851, 847, 853, 847, 847, 852, 81, 81, + 854, 854, 854, 854, 854, 854, 854, 854, 854, 854, 849, 855, 849, 851, + 851, 851, 81, 856, 856, 856, 856, 856, 856, 856, 856, 856, 856, 856, 856, + 856, 856, 856, 856, 856, 856, 856, 856, 81, 81, 81, 857, 857, 857, 857, + 857, 857, 857, 857, 857, 857, 81, 857, 857, 857, 857, 857, 857, 857, 857, + 857, 858, 858, 858, 859, 859, 859, 858, 858, 859, 860, 861, 859, 862, + 862, 863, 862, 862, 863, 859, 81, 864, 864, 864, 864, 864, 864, 864, 81, + 864, 81, 864, 864, 864, 864, 81, 864, 864, 864, 864, 864, 864, 864, 864, + 864, 864, 864, 864, 864, 864, 864, 81, 864, 864, 865, 81, 81, 81, 81, 81, + 81, 866, 866, 866, 866, 866, 866, 866, 866, 866, 866, 866, 866, 866, 866, + 866, 867, 868, 868, 868, 867, 867, 867, 867, 867, 867, 869, 870, 81, 81, + 81, 81, 81, 871, 871, 871, 871, 871, 871, 871, 871, 871, 871, 81, 81, 81, + 81, 81, 81, 872, 872, 873, 873, 81, 874, 874, 874, 874, 874, 874, 874, + 874, 81, 81, 874, 874, 81, 81, 874, 874, 874, 874, 874, 874, 874, 874, + 874, 874, 874, 874, 874, 874, 81, 874, 874, 874, 874, 874, 874, 874, 81, + 874, 874, 81, 874, 874, 874, 874, 874, 81, 875, 876, 874, 873, 873, 872, + 873, 873, 873, 873, 81, 81, 873, 873, 81, 81, 873, 873, 877, 81, 81, 874, + 81, 81, 81, 81, 81, 81, 873, 81, 81, 81, 81, 81, 874, 874, 874, 874, 874, + 873, 873, 81, 81, 878, 878, 878, 878, 878, 878, 878, 81, 81, 81, 879, + 879, 879, 879, 879, 879, 879, 879, 879, 879, 879, 879, 879, 880, 880, + 880, 881, 881, 881, 881, 881, 881, 881, 881, 880, 880, 882, 881, 881, + 880, 883, 879, 879, 879, 879, 884, 884, 884, 884, 885, 886, 886, 886, + 886, 886, 886, 886, 886, 886, 886, 81, 884, 81, 885, 887, 879, 888, 888, + 888, 888, 888, 888, 888, 888, 889, 889, 889, 890, 890, 890, 890, 890, + 890, 889, 890, 889, 889, 889, 889, 890, 890, 889, 891, 892, 888, 888, + 893, 888, 894, 894, 894, 894, 894, 894, 894, 894, 894, 894, 81, 81, 81, + 81, 81, 81, 895, 895, 895, 895, 895, 895, 895, 895, 895, 895, 895, 895, + 895, 895, 895, 896, 896, 896, 897, 897, 897, 897, 81, 81, 896, 896, 896, + 896, 897, 897, 896, 898, 899, 900, 901, 901, 902, 902, 903, 903, 903, + 901, 901, 901, 901, 901, 901, 901, 901, 901, 901, 901, 901, 901, 901, + 901, 895, 895, 895, 895, 897, 897, 81, 81, 904, 904, 904, 904, 904, 904, + 904, 904, 905, 905, 905, 906, 906, 906, 906, 906, 906, 906, 906, 905, + 905, 906, 905, 907, 906, 908, 908, 909, 904, 81, 81, 81, 910, 910, 910, + 910, 910, 910, 910, 910, 910, 910, 81, 81, 81, 81, 81, 81, 911, 911, 911, + 911, 911, 911, 911, 911, 911, 911, 911, 911, 911, 81, 81, 81, 912, 912, + 912, 912, 912, 912, 912, 912, 912, 912, 912, 913, 914, 913, 914, 914, + 913, 913, 913, 913, 913, 913, 915, 916, 912, 81, 81, 81, 81, 81, 81, 81, + 917, 917, 917, 917, 917, 917, 917, 917, 917, 917, 81, 81, 81, 81, 81, 81, + 918, 918, 918, 918, 918, 918, 918, 918, 918, 918, 918, 81, 81, 919, 919, + 919, 920, 920, 919, 919, 919, 919, 920, 919, 919, 919, 919, 921, 81, 81, + 81, 81, 922, 922, 922, 922, 922, 922, 922, 922, 922, 922, 923, 923, 924, + 924, 924, 925, 926, 926, 926, 926, 926, 926, 926, 926, 926, 926, 926, + 926, 927, 927, 927, 928, 928, 928, 928, 928, 928, 928, 928, 928, 927, + 929, 930, 931, 81, 81, 81, 81, 932, 932, 932, 932, 932, 932, 932, 932, + 933, 933, 933, 933, 933, 933, 933, 933, 934, 934, 934, 934, 934, 934, + 934, 934, 934, 934, 935, 935, 935, 935, 935, 935, 935, 935, 935, 81, 81, + 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 936, 937, 937, 937, 937, 937, + 937, 937, 937, 81, 81, 937, 937, 937, 937, 937, 937, 937, 938, 938, 938, + 939, 939, 939, 939, 81, 81, 939, 939, 938, 938, 938, 938, 940, 937, 941, + 937, 938, 81, 81, 81, 942, 943, 943, 943, 943, 943, 943, 944, 944, 943, + 943, 942, 942, 942, 942, 942, 942, 942, 942, 942, 942, 942, 942, 942, + 942, 942, 942, 943, 945, 943, 943, 943, 943, 946, 942, 943, 943, 943, + 943, 947, 948, 949, 949, 949, 949, 947, 948, 945, 950, 951, 951, 951, + 951, 951, 951, 952, 952, 951, 951, 951, 950, 950, 950, 950, 950, 950, + 950, 950, 950, 950, 950, 950, 950, 950, 951, 951, 951, 951, 951, 951, + 951, 951, 951, 951, 951, 951, 951, 952, 951, 953, 954, 954, 954, 950, + 955, 955, 955, 954, 954, 81, 81, 81, 81, 81, 956, 956, 956, 956, 956, + 956, 956, 956, 956, 81, 81, 81, 81, 81, 81, 81, 957, 957, 957, 957, 957, + 957, 957, 957, 957, 81, 957, 957, 957, 957, 957, 957, 957, 957, 957, 957, + 957, 957, 957, 958, 959, 959, 959, 959, 959, 959, 959, 81, 959, 959, 959, + 959, 959, 959, 958, 960, 957, 961, 961, 961, 961, 961, 81, 81, 962, 962, + 962, 962, 962, 962, 962, 962, 962, 962, 963, 963, 963, 963, 963, 963, + 963, 963, 963, 963, 963, 963, 963, 963, 963, 963, 963, 963, 963, 81, 81, + 81, 964, 965, 966, 966, 966, 966, 966, 966, 966, 966, 966, 966, 966, 966, + 966, 966, 81, 81, 967, 967, 967, 967, 967, 967, 967, 967, 967, 967, 967, + 967, 967, 967, 81, 968, 967, 967, 967, 967, 967, 967, 967, 968, 967, 967, + 968, 967, 967, 81, 969, 969, 969, 969, 969, 969, 969, 81, 969, 969, 81, + 969, 969, 969, 969, 969, 969, 969, 969, 969, 969, 969, 969, 969, 969, + 970, 970, 970, 970, 970, 970, 81, 81, 81, 970, 81, 970, 970, 81, 970, + 970, 970, 971, 970, 972, 972, 969, 970, 973, 973, 973, 973, 973, 973, + 973, 973, 973, 973, 81, 81, 81, 81, 81, 81, 974, 974, 974, 974, 974, 974, + 81, 974, 974, 81, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, + 974, 974, 974, 974, 974, 975, 975, 975, 975, 975, 81, 976, 976, 81, 975, + 975, 976, 975, 977, 974, 81, 81, 81, 81, 81, 81, 81, 978, 978, 978, 978, + 978, 978, 978, 978, 978, 978, 81, 81, 81, 81, 81, 81, 979, 979, 979, 979, + 979, 979, 979, 979, 979, 979, 979, 980, 980, 981, 981, 982, 982, 81, 81, + 81, 81, 81, 81, 81, 243, 243, 243, 243, 243, 243, 243, 243, 243, 243, + 243, 243, 243, 244, 244, 244, 244, 244, 244, 244, 244, 983, 983, 983, + 983, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, 244, + 244, 244, 244, 244, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, + 984, 985, 985, 985, 985, 985, 985, 985, 985, 985, 985, 81, 81, 81, 81, + 81, 81, 986, 986, 986, 986, 986, 986, 986, 986, 986, 986, 986, 986, 986, + 986, 986, 81, 987, 987, 987, 987, 987, 81, 81, 81, 985, 985, 985, 985, + 81, 81, 81, 81, 988, 988, 988, 988, 988, 988, 988, 988, 989, 989, 989, + 990, 990, 990, 988, 988, 988, 988, 990, 988, 988, 988, 989, 990, 989, + 990, 988, 988, 988, 988, 988, 988, 988, 989, 990, 990, 988, 988, 988, + 988, 988, 988, 988, 988, 988, 988, 988, 81, 991, 991, 991, 991, 991, 991, + 991, 992, 993, 81, 81, 81, 81, 81, 81, 81, 994, 994, 994, 994, 994, 994, + 994, 994, 994, 994, 994, 994, 994, 994, 995, 996, 994, 994, 994, 994, + 994, 994, 994, 81, 610, 81, 81, 81, 81, 81, 81, 81, 997, 997, 997, 997, + 997, 997, 997, 997, 997, 997, 997, 997, 997, 997, 997, 81, 998, 998, 998, + 998, 998, 998, 998, 998, 998, 998, 81, 81, 81, 81, 999, 999, 1000, 1000, + 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, + 81, 81, 1001, 1001, 1001, 1001, 1001, 1002, 81, 81, 1003, 1003, 1003, + 1003, 1003, 1003, 1003, 1003, 1004, 1004, 1004, 1004, 1004, 1004, 1004, + 1005, 1005, 1005, 1006, 1006, 1007, 1007, 1007, 1007, 1008, 1008, 1008, + 1008, 1005, 1007, 81, 81, 1009, 1009, 1009, 1009, 1009, 1009, 1009, 1009, + 1009, 1009, 81, 1010, 1010, 1010, 1010, 1010, 1010, 1010, 81, 1003, 1003, + 1003, 1003, 1003, 81, 81, 81, 81, 81, 1003, 1003, 1003, 1011, 1011, 1011, + 1011, 1011, 1011, 1011, 1011, 1012, 1012, 1012, 1012, 1012, 1012, 1012, + 1012, 1013, 1013, 1013, 1013, 1013, 1013, 1013, 1013, 1013, 1013, 1013, + 1013, 1013, 1013, 1013, 1014, 1014, 1015, 1015, 81, 81, 81, 81, 81, 1016, + 1016, 1016, 1016, 1016, 1016, 1016, 1016, 1016, 1016, 1016, 81, 81, 81, + 81, 1017, 1016, 1018, 1018, 1018, 1018, 1018, 1018, 1018, 1018, 1018, + 1018, 1018, 1018, 1018, 1018, 1018, 81, 81, 81, 81, 81, 81, 81, 1017, + 1017, 1017, 1017, 1019, 1019, 1019, 1019, 1019, 1019, 1019, 1019, 1019, + 1019, 1019, 1019, 1019, 1020, 1021, 582, 1022, 81, 81, 81, 81, 1023, + 1023, 1023, 1023, 1023, 1023, 1023, 1023, 1023, 1023, 1023, 81, 81, 81, + 81, 81, 581, 576, 576, 576, 576, 576, 576, 576, 576, 576, 576, 576, 576, + 576, 576, 81, 575, 575, 575, 81, 81, 81, 81, 81, 81, 81, 81, 81, 580, + 580, 580, 580, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, + 1024, 1024, 1024, 81, 81, 81, 81, 1025, 1025, 1025, 1025, 1025, 1025, + 1025, 1025, 1025, 1025, 1025, 81, 81, 81, 81, 81, 1025, 1025, 1025, 1025, + 1025, 81, 81, 81, 1025, 81, 81, 81, 81, 81, 81, 81, 1025, 1025, 81, 81, + 1026, 1027, 1028, 1029, 505, 505, 505, 505, 81, 81, 81, 81, 318, 318, + 318, 318, 318, 318, 81, 81, 318, 318, 318, 318, 318, 318, 318, 81, 81, + 318, 318, 318, 318, 318, 318, 318, 318, 318, 318, 318, 318, 1030, 1030, + 453, 453, 453, 318, 318, 318, 1031, 1030, 1030, 1030, 1030, 1030, 505, + 505, 505, 505, 505, 505, 505, 505, 156, 156, 156, 156, 156, 156, 156, + 156, 318, 318, 96, 96, 96, 96, 96, 156, 156, 318, 318, 318, 318, 318, + 318, 96, 96, 96, 96, 318, 318, 318, 81, 81, 81, 81, 81, 81, 81, 727, 727, + 1032, 1032, 1032, 727, 81, 81, 621, 621, 621, 621, 81, 81, 81, 81, 621, + 81, 81, 81, 81, 81, 81, 81, 512, 512, 512, 512, 512, 512, 512, 512, 512, + 512, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, + 49, 512, 512, 512, 512, 512, 512, 512, 512, 512, 512, 49, 49, 49, 49, 49, + 49, 49, 81, 49, 49, 49, 49, 49, 49, 512, 81, 512, 512, 81, 81, 512, 81, + 81, 512, 512, 81, 81, 512, 512, 512, 512, 81, 512, 512, 49, 49, 81, 49, + 81, 49, 49, 49, 49, 49, 49, 49, 81, 49, 49, 49, 49, 49, 49, 49, 512, 512, + 81, 512, 512, 512, 512, 81, 81, 512, 512, 512, 512, 512, 512, 512, 512, + 81, 512, 512, 512, 512, 512, 512, 512, 81, 49, 49, 512, 512, 81, 512, + 512, 512, 512, 81, 512, 512, 512, 512, 512, 81, 512, 81, 81, 81, 512, + 512, 512, 512, 512, 512, 512, 81, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, + 49, 49, 81, 81, 512, 1033, 49, 49, 49, 49, 49, 49, 49, 49, 49, 498, 49, + 49, 49, 49, 49, 49, 512, 512, 512, 512, 512, 512, 512, 512, 512, 1033, + 49, 49, 49, 49, 49, 49, 49, 49, 49, 498, 49, 49, 512, 512, 512, 512, 512, + 1033, 49, 49, 49, 49, 49, 49, 49, 49, 49, 498, 49, 49, 49, 49, 49, 49, + 512, 512, 512, 512, 512, 512, 512, 512, 512, 1033, 49, 498, 49, 49, 49, + 49, 49, 49, 49, 49, 512, 49, 81, 81, 1034, 1034, 1034, 1034, 1034, 1034, + 1034, 1034, 1034, 1034, 1035, 1035, 1035, 1035, 1035, 1035, 1035, 1035, + 1036, 1036, 1036, 1036, 1036, 1036, 1036, 1036, 1036, 1036, 1036, 1036, + 1036, 1036, 1036, 1035, 1035, 1035, 1035, 1036, 1036, 1036, 1036, 1036, + 1036, 1036, 1036, 1036, 1036, 1035, 1035, 1035, 1035, 1035, 1035, 1035, + 1035, 1036, 1035, 1035, 1035, 1035, 1035, 1035, 1036, 1035, 1035, 1037, + 1037, 1037, 1037, 1038, 81, 81, 81, 81, 81, 81, 81, 1036, 1036, 1036, + 1036, 1036, 81, 1036, 1036, 1036, 1036, 1036, 1036, 1036, 1039, 1039, + 1039, 1039, 1039, 1039, 1039, 81, 1039, 1039, 1039, 1039, 1039, 1039, + 1039, 1039, 1039, 81, 81, 1039, 1039, 1039, 1039, 1039, 1039, 1039, 81, + 1039, 1039, 81, 1039, 1039, 1039, 1039, 1039, 81, 81, 81, 81, 81, 1040, + 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, + 81, 81, 81, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1042, 1042, 1042, + 1042, 1042, 1042, 1042, 81, 81, 1043, 1043, 1043, 1043, 1043, 1043, 1043, + 1043, 1043, 1043, 81, 81, 81, 81, 1040, 1044, 1045, 1045, 1045, 1045, + 1045, 1045, 1045, 1045, 1045, 1045, 1045, 1045, 1046, 1046, 1046, 1046, + 1047, 1047, 1047, 1047, 1047, 1047, 1047, 1047, 1047, 1047, 81, 81, 81, + 81, 81, 1048, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, + 1049, 1049, 1049, 81, 81, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, + 1050, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 81, 1052, 1052, 1052, + 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1053, 1053, 1053, 1053, 1053, + 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, + 1053, 1054, 1054, 1054, 1054, 1054, 1054, 1055, 1056, 81, 81, 81, 81, + 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 81, 81, 81, + 81, 1058, 1058, 81, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, + 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1060, 1059, + 1059, 1059, 1061, 1059, 1059, 1059, 1059, 81, 81, 81, 1059, 1059, 1059, + 1059, 1059, 1059, 1062, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 81, 81, + 146, 146, 146, 146, 81, 146, 146, 146, 81, 146, 146, 81, 146, 81, 81, + 146, 81, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 81, 146, 146, + 146, 146, 81, 146, 81, 146, 81, 81, 81, 81, 81, 81, 146, 81, 81, 81, 81, + 146, 81, 146, 81, 146, 81, 146, 146, 146, 81, 146, 81, 146, 81, 146, 81, + 146, 81, 146, 146, 146, 146, 81, 146, 81, 146, 146, 81, 146, 146, 146, + 146, 146, 146, 146, 146, 146, 81, 81, 81, 81, 81, 146, 146, 146, 81, 146, + 146, 146, 132, 132, 81, 81, 81, 81, 81, 81, 531, 531, 531, 531, 527, 531, + 531, 531, 531, 531, 531, 531, 531, 531, 531, 531, 531, 531, 531, 531, + 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, + 531, 531, 531, 531, 531, 531, 531, 1063, 1063, 531, 531, 531, 531, 531, + 531, 531, 531, 531, 531, 531, 531, 531, 531, 527, 531, 531, 531, 531, + 531, 531, 1063, 1063, 47, 47, 47, 521, 521, 1063, 1063, 1063, 532, 532, + 532, 532, 532, 532, 318, 40, 532, 532, 40, 40, 40, 1063, 1063, 1063, 532, + 532, 532, 532, 532, 532, 1064, 532, 532, 1064, 1064, 1064, 1064, 1064, + 1064, 1064, 1064, 1064, 1064, 532, 532, 532, 532, 532, 532, 532, 532, + 532, 532, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1065, + 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1066, 587, 587, + 1063, 1063, 1063, 1063, 1063, 587, 587, 587, 587, 1063, 1063, 1063, 1063, + 587, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 587, 587, 1063, 1063, + 1063, 1063, 1063, 1063, 527, 527, 527, 527, 527, 527, 1063, 1063, 527, + 531, 531, 531, 531, 531, 531, 531, 531, 531, 531, 531, 531, 527, 527, + 527, 527, 527, 527, 527, 527, 527, 531, 527, 527, 527, 527, 527, 527, + 531, 527, 527, 527, 527, 527, 527, 527, 538, 527, 527, 527, 527, 527, + 527, 531, 531, 531, 531, 531, 531, 531, 531, 40, 40, 531, 531, 527, 527, + 527, 527, 527, 530, 530, 527, 527, 527, 527, 527, 530, 527, 527, 527, + 527, 527, 538, 538, 538, 527, 527, 538, 527, 527, 538, 536, 536, 531, + 531, 527, 527, 531, 531, 531, 527, 531, 531, 531, 527, 527, 527, 1067, + 1067, 1067, 1067, 1067, 527, 527, 527, 527, 527, 527, 527, 531, 527, 531, + 538, 538, 527, 527, 538, 538, 538, 538, 538, 538, 538, 538, 538, 538, + 538, 527, 527, 527, 527, 527, 527, 527, 527, 527, 527, 527, 527, 527, + 538, 538, 538, 527, 527, 527, 538, 527, 527, 527, 527, 538, 538, 538, + 527, 538, 538, 538, 527, 527, 527, 527, 527, 527, 527, 538, 527, 538, + 527, 527, 527, 527, 527, 527, 530, 527, 530, 527, 530, 527, 527, 527, + 527, 527, 538, 527, 527, 527, 527, 530, 527, 530, 530, 527, 527, 527, + 527, 527, 527, 527, 527, 527, 527, 531, 531, 527, 530, 530, 530, 530, + 530, 530, 530, 527, 527, 527, 527, 527, 527, 527, 527, 530, 530, 530, + 530, 530, 530, 527, 527, 527, 527, 527, 530, 530, 530, 530, 530, 530, + 530, 530, 530, 530, 530, 530, 40, 40, 40, 40, 531, 527, 527, 527, 527, + 531, 531, 531, 531, 531, 536, 536, 531, 531, 531, 531, 538, 531, 531, + 531, 531, 531, 536, 531, 531, 531, 531, 538, 538, 531, 531, 531, 531, + 531, 40, 40, 40, 40, 40, 40, 40, 40, 531, 531, 531, 531, 40, 40, 531, + 527, 527, 527, 527, 527, 527, 527, 527, 527, 527, 538, 538, 538, 527, + 527, 527, 538, 538, 538, 538, 538, 40, 40, 40, 40, 40, 40, 540, 540, 540, + 1068, 1068, 1068, 40, 40, 40, 40, 527, 527, 527, 538, 527, 527, 527, 527, + 527, 527, 527, 527, 538, 538, 538, 527, 538, 527, 527, 527, 527, 527, + 531, 531, 531, 531, 531, 531, 538, 531, 531, 531, 527, 527, 527, 531, + 531, 527, 1063, 1063, 531, 531, 531, 527, 527, 1063, 1063, 1063, 531, + 531, 531, 531, 527, 527, 527, 527, 527, 527, 527, 1063, 1063, 1063, 1063, + 1063, 40, 40, 40, 40, 1063, 1063, 1063, 1063, 40, 40, 40, 40, 40, 531, + 531, 531, 531, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 527, 527, 527, + 527, 1063, 1063, 1063, 1063, 40, 40, 1063, 1063, 1063, 1063, 1063, 1063, + 40, 40, 40, 40, 40, 40, 1063, 1063, 40, 40, 40, 40, 1063, 527, 527, 538, + 527, 527, 527, 527, 527, 527, 538, 527, 538, 538, 527, 527, 538, 538, + 538, 527, 527, 527, 1063, 527, 527, 527, 527, 1063, 1063, 1063, 527, 527, + 527, 527, 527, 527, 527, 527, 527, 1063, 1063, 527, 527, 527, 527, 527, + 527, 1063, 1063, 1063, 527, 527, 527, 527, 527, 527, 527, 538, 538, 527, + 538, 538, 527, 538, 527, 527, 527, 527, 527, 527, 527, 1063, 1063, 538, + 538, 538, 527, 538, 538, 538, 538, 538, 538, 538, 538, 538, 538, 538, + 538, 538, 527, 527, 1063, 1063, 1063, 1063, 1063, 1063, 81, 81, 594, 594, + 594, 594, 594, 594, 594, 595, 594, 594, 594, 594, 594, 595, 595, 595, + 594, 595, 595, 595, 595, 595, 595, 595, 595, 595, 595, 595, 595, 595, 81, + 81, 81, 505, 81, 81, 81, 81, 81, 81, 505, 505, 505, 505, 505, 505, 505, + 505, 673, 673, 673, 673, 673, 673, 81, 81, }; /* decomposition data */ @@ -4534,118 +4594,118 @@ static const unsigned short decomp_data[] = { 78, 266, 79, 266, 80, 266, 81, 266, 82, 266, 83, 266, 84, 266, 85, 266, 86, 266, 87, 266, 88, 266, 89, 266, 90, 522, 72, 86, 522, 77, 86, 522, 83, 68, 522, 83, 83, 778, 80, 80, 86, 522, 87, 67, 515, 77, 67, 515, 77, - 68, 522, 68, 74, 522, 12411, 12363, 522, 12467, 12467, 266, 12469, 266, - 25163, 266, 23383, 266, 21452, 266, 12487, 266, 20108, 266, 22810, 266, - 35299, 266, 22825, 266, 20132, 266, 26144, 266, 28961, 266, 26009, 266, - 21069, 266, 24460, 266, 20877, 266, 26032, 266, 21021, 266, 32066, 266, - 29983, 266, 36009, 266, 22768, 266, 21561, 266, 28436, 266, 25237, 266, - 25429, 266, 19968, 266, 19977, 266, 36938, 266, 24038, 266, 20013, 266, - 21491, 266, 25351, 266, 36208, 266, 25171, 266, 31105, 266, 31354, 266, - 21512, 266, 28288, 266, 26377, 266, 26376, 266, 30003, 266, 21106, 266, - 21942, 266, 37197, 770, 12308, 26412, 12309, 770, 12308, 19977, 12309, - 770, 12308, 20108, 12309, 770, 12308, 23433, 12309, 770, 12308, 28857, - 12309, 770, 12308, 25171, 12309, 770, 12308, 30423, 12309, 770, 12308, - 21213, 12309, 770, 12308, 25943, 12309, 263, 24471, 263, 21487, 256, - 20029, 256, 20024, 256, 20033, 256, 55360, 56610, 256, 20320, 256, 20398, - 256, 20411, 256, 20482, 256, 20602, 256, 20633, 256, 20711, 256, 20687, - 256, 13470, 256, 55361, 56890, 256, 20813, 256, 20820, 256, 20836, 256, - 20855, 256, 55361, 56604, 256, 13497, 256, 20839, 256, 20877, 256, 55361, - 56651, 256, 20887, 256, 20900, 256, 20172, 256, 20908, 256, 20917, 256, - 55396, 56799, 256, 20981, 256, 20995, 256, 13535, 256, 21051, 256, 21062, - 256, 21106, 256, 21111, 256, 13589, 256, 21191, 256, 21193, 256, 21220, - 256, 21242, 256, 21253, 256, 21254, 256, 21271, 256, 21321, 256, 21329, - 256, 21338, 256, 21363, 256, 21373, 256, 21375, 256, 21375, 256, 21375, - 256, 55362, 56876, 256, 28784, 256, 21450, 256, 21471, 256, 55362, 57187, - 256, 21483, 256, 21489, 256, 21510, 256, 21662, 256, 21560, 256, 21576, - 256, 21608, 256, 21666, 256, 21750, 256, 21776, 256, 21843, 256, 21859, - 256, 21892, 256, 21892, 256, 21913, 256, 21931, 256, 21939, 256, 21954, - 256, 22294, 256, 22022, 256, 22295, 256, 22097, 256, 22132, 256, 20999, - 256, 22766, 256, 22478, 256, 22516, 256, 22541, 256, 22411, 256, 22578, - 256, 22577, 256, 22700, 256, 55365, 56548, 256, 22770, 256, 22775, 256, - 22790, 256, 22810, 256, 22818, 256, 22882, 256, 55365, 57000, 256, 55365, - 57066, 256, 23020, 256, 23067, 256, 23079, 256, 23000, 256, 23142, 256, - 14062, 256, 14076, 256, 23304, 256, 23358, 256, 23358, 256, 55366, 56776, - 256, 23491, 256, 23512, 256, 23527, 256, 23539, 256, 55366, 57112, 256, - 23551, 256, 23558, 256, 24403, 256, 23586, 256, 14209, 256, 23648, 256, - 23662, 256, 23744, 256, 23693, 256, 55367, 56804, 256, 23875, 256, 55367, - 56806, 256, 23918, 256, 23915, 256, 23932, 256, 24033, 256, 24034, 256, - 14383, 256, 24061, 256, 24104, 256, 24125, 256, 24169, 256, 14434, 256, - 55368, 56707, 256, 14460, 256, 24240, 256, 24243, 256, 24246, 256, 24266, - 256, 55400, 57234, 256, 24318, 256, 55368, 57137, 256, 55368, 57137, 256, - 33281, 256, 24354, 256, 24354, 256, 14535, 256, 55372, 57016, 256, 55384, - 56794, 256, 24418, 256, 24427, 256, 14563, 256, 24474, 256, 24525, 256, - 24535, 256, 24569, 256, 24705, 256, 14650, 256, 14620, 256, 24724, 256, - 55369, 57044, 256, 24775, 256, 24904, 256, 24908, 256, 24910, 256, 24908, - 256, 24954, 256, 24974, 256, 25010, 256, 24996, 256, 25007, 256, 25054, - 256, 25074, 256, 25078, 256, 25104, 256, 25115, 256, 25181, 256, 25265, - 256, 25300, 256, 25424, 256, 55370, 57100, 256, 25405, 256, 25340, 256, - 25448, 256, 25475, 256, 25572, 256, 55370, 57329, 256, 25634, 256, 25541, - 256, 25513, 256, 14894, 256, 25705, 256, 25726, 256, 25757, 256, 25719, - 256, 14956, 256, 25935, 256, 25964, 256, 55372, 56330, 256, 26083, 256, - 26360, 256, 26185, 256, 15129, 256, 26257, 256, 15112, 256, 15076, 256, - 20882, 256, 20885, 256, 26368, 256, 26268, 256, 32941, 256, 17369, 256, - 26391, 256, 26395, 256, 26401, 256, 26462, 256, 26451, 256, 55372, 57283, - 256, 15177, 256, 26618, 256, 26501, 256, 26706, 256, 26757, 256, 55373, - 56429, 256, 26766, 256, 26655, 256, 26900, 256, 15261, 256, 26946, 256, - 27043, 256, 27114, 256, 27304, 256, 55373, 56995, 256, 27355, 256, 15384, - 256, 27425, 256, 55374, 56487, 256, 27476, 256, 15438, 256, 27506, 256, - 27551, 256, 27578, 256, 27579, 256, 55374, 56973, 256, 55367, 56587, 256, - 55374, 57082, 256, 27726, 256, 55375, 56508, 256, 27839, 256, 27853, 256, - 27751, 256, 27926, 256, 27966, 256, 28023, 256, 27969, 256, 28009, 256, - 28024, 256, 28037, 256, 55375, 56606, 256, 27956, 256, 28207, 256, 28270, - 256, 15667, 256, 28363, 256, 28359, 256, 55375, 57041, 256, 28153, 256, - 28526, 256, 55375, 57182, 256, 55375, 57230, 256, 28614, 256, 28729, 256, - 28702, 256, 28699, 256, 15766, 256, 28746, 256, 28797, 256, 28791, 256, - 28845, 256, 55361, 56613, 256, 28997, 256, 55376, 56931, 256, 29084, 256, - 55376, 57259, 256, 29224, 256, 29237, 256, 29264, 256, 55377, 56840, 256, - 29312, 256, 29333, 256, 55377, 57141, 256, 55378, 56340, 256, 29562, 256, - 29579, 256, 16044, 256, 29605, 256, 16056, 256, 16056, 256, 29767, 256, - 29788, 256, 29809, 256, 29829, 256, 29898, 256, 16155, 256, 29988, 256, - 55379, 56374, 256, 30014, 256, 55379, 56466, 256, 30064, 256, 55368, - 56735, 256, 30224, 256, 55379, 57249, 256, 55379, 57272, 256, 55380, - 56388, 256, 16380, 256, 16392, 256, 30452, 256, 55380, 56563, 256, 55380, - 56562, 256, 55380, 56601, 256, 55380, 56627, 256, 30494, 256, 30495, 256, - 30495, 256, 30538, 256, 16441, 256, 30603, 256, 16454, 256, 16534, 256, - 55381, 56349, 256, 30798, 256, 30860, 256, 30924, 256, 16611, 256, 55381, - 56870, 256, 31062, 256, 55381, 56986, 256, 55381, 57029, 256, 31119, 256, - 31211, 256, 16687, 256, 31296, 256, 31306, 256, 31311, 256, 55382, 56700, - 256, 55382, 56999, 256, 55382, 56999, 256, 31470, 256, 16898, 256, 55382, - 57259, 256, 31686, 256, 31689, 256, 16935, 256, 55383, 56448, 256, 31954, - 256, 17056, 256, 31976, 256, 31971, 256, 32000, 256, 55383, 57222, 256, - 32099, 256, 17153, 256, 32199, 256, 32258, 256, 32325, 256, 17204, 256, - 55384, 56872, 256, 55384, 56903, 256, 17241, 256, 55384, 57049, 256, - 32634, 256, 55384, 57150, 256, 32661, 256, 32762, 256, 32773, 256, 55385, - 56538, 256, 55385, 56611, 256, 32864, 256, 55385, 56744, 256, 32880, 256, - 55372, 57183, 256, 17365, 256, 32946, 256, 33027, 256, 17419, 256, 33086, - 256, 23221, 256, 55385, 57255, 256, 55385, 57269, 256, 55372, 57235, 256, - 55372, 57244, 256, 33281, 256, 33284, 256, 36766, 256, 17515, 256, 33425, - 256, 33419, 256, 33437, 256, 21171, 256, 33457, 256, 33459, 256, 33469, - 256, 33510, 256, 55386, 57148, 256, 33509, 256, 33565, 256, 33635, 256, - 33709, 256, 33571, 256, 33725, 256, 33767, 256, 33879, 256, 33619, 256, - 33738, 256, 33740, 256, 33756, 256, 55387, 56374, 256, 55387, 56683, 256, - 55387, 56533, 256, 17707, 256, 34033, 256, 34035, 256, 34070, 256, 55388, - 57290, 256, 34148, 256, 55387, 57132, 256, 17757, 256, 17761, 256, 55387, - 57265, 256, 55388, 56530, 256, 17771, 256, 34384, 256, 34396, 256, 34407, - 256, 34409, 256, 34473, 256, 34440, 256, 34574, 256, 34530, 256, 34681, - 256, 34600, 256, 34667, 256, 34694, 256, 17879, 256, 34785, 256, 34817, - 256, 17913, 256, 34912, 256, 34915, 256, 55389, 56935, 256, 35031, 256, - 35038, 256, 17973, 256, 35066, 256, 13499, 256, 55390, 56494, 256, 55390, - 56678, 256, 18110, 256, 18119, 256, 35488, 256, 35565, 256, 35722, 256, - 35925, 256, 55391, 56488, 256, 36011, 256, 36033, 256, 36123, 256, 36215, - 256, 55391, 57135, 256, 55362, 56324, 256, 36299, 256, 36284, 256, 36336, - 256, 55362, 56542, 256, 36564, 256, 36664, 256, 55393, 56786, 256, 55393, - 56813, 256, 37012, 256, 37105, 256, 37137, 256, 55393, 57134, 256, 37147, - 256, 37432, 256, 37591, 256, 37592, 256, 37500, 256, 37881, 256, 37909, - 256, 55394, 57338, 256, 38283, 256, 18837, 256, 38327, 256, 55395, 56695, - 256, 18918, 256, 38595, 256, 23986, 256, 38691, 256, 55396, 56645, 256, - 55396, 56858, 256, 19054, 256, 19062, 256, 38880, 256, 55397, 56330, 256, - 19122, 256, 55397, 56470, 256, 38923, 256, 38923, 256, 38953, 256, 55397, - 56758, 256, 39138, 256, 19251, 256, 39209, 256, 39335, 256, 39362, 256, - 39422, 256, 19406, 256, 55398, 57136, 256, 39698, 256, 40000, 256, 40189, - 256, 19662, 256, 19693, 256, 40295, 256, 55400, 56526, 256, 19704, 256, - 55400, 56581, 256, 55400, 56846, 256, 55400, 56977, 256, 40635, 256, - 19798, 256, 40697, 256, 40702, 256, 40709, 256, 40719, 256, 40726, 256, - 40763, 256, 55401, 56832, + 68, 515, 77, 82, 522, 68, 74, 522, 12411, 12363, 522, 12467, 12467, 266, + 12469, 266, 25163, 266, 23383, 266, 21452, 266, 12487, 266, 20108, 266, + 22810, 266, 35299, 266, 22825, 266, 20132, 266, 26144, 266, 28961, 266, + 26009, 266, 21069, 266, 24460, 266, 20877, 266, 26032, 266, 21021, 266, + 32066, 266, 29983, 266, 36009, 266, 22768, 266, 21561, 266, 28436, 266, + 25237, 266, 25429, 266, 19968, 266, 19977, 266, 36938, 266, 24038, 266, + 20013, 266, 21491, 266, 25351, 266, 36208, 266, 25171, 266, 31105, 266, + 31354, 266, 21512, 266, 28288, 266, 26377, 266, 26376, 266, 30003, 266, + 21106, 266, 21942, 266, 37197, 770, 12308, 26412, 12309, 770, 12308, + 19977, 12309, 770, 12308, 20108, 12309, 770, 12308, 23433, 12309, 770, + 12308, 28857, 12309, 770, 12308, 25171, 12309, 770, 12308, 30423, 12309, + 770, 12308, 21213, 12309, 770, 12308, 25943, 12309, 263, 24471, 263, + 21487, 256, 20029, 256, 20024, 256, 20033, 256, 55360, 56610, 256, 20320, + 256, 20398, 256, 20411, 256, 20482, 256, 20602, 256, 20633, 256, 20711, + 256, 20687, 256, 13470, 256, 55361, 56890, 256, 20813, 256, 20820, 256, + 20836, 256, 20855, 256, 55361, 56604, 256, 13497, 256, 20839, 256, 20877, + 256, 55361, 56651, 256, 20887, 256, 20900, 256, 20172, 256, 20908, 256, + 20917, 256, 55396, 56799, 256, 20981, 256, 20995, 256, 13535, 256, 21051, + 256, 21062, 256, 21106, 256, 21111, 256, 13589, 256, 21191, 256, 21193, + 256, 21220, 256, 21242, 256, 21253, 256, 21254, 256, 21271, 256, 21321, + 256, 21329, 256, 21338, 256, 21363, 256, 21373, 256, 21375, 256, 21375, + 256, 21375, 256, 55362, 56876, 256, 28784, 256, 21450, 256, 21471, 256, + 55362, 57187, 256, 21483, 256, 21489, 256, 21510, 256, 21662, 256, 21560, + 256, 21576, 256, 21608, 256, 21666, 256, 21750, 256, 21776, 256, 21843, + 256, 21859, 256, 21892, 256, 21892, 256, 21913, 256, 21931, 256, 21939, + 256, 21954, 256, 22294, 256, 22022, 256, 22295, 256, 22097, 256, 22132, + 256, 20999, 256, 22766, 256, 22478, 256, 22516, 256, 22541, 256, 22411, + 256, 22578, 256, 22577, 256, 22700, 256, 55365, 56548, 256, 22770, 256, + 22775, 256, 22790, 256, 22810, 256, 22818, 256, 22882, 256, 55365, 57000, + 256, 55365, 57066, 256, 23020, 256, 23067, 256, 23079, 256, 23000, 256, + 23142, 256, 14062, 256, 14076, 256, 23304, 256, 23358, 256, 23358, 256, + 55366, 56776, 256, 23491, 256, 23512, 256, 23527, 256, 23539, 256, 55366, + 57112, 256, 23551, 256, 23558, 256, 24403, 256, 23586, 256, 14209, 256, + 23648, 256, 23662, 256, 23744, 256, 23693, 256, 55367, 56804, 256, 23875, + 256, 55367, 56806, 256, 23918, 256, 23915, 256, 23932, 256, 24033, 256, + 24034, 256, 14383, 256, 24061, 256, 24104, 256, 24125, 256, 24169, 256, + 14434, 256, 55368, 56707, 256, 14460, 256, 24240, 256, 24243, 256, 24246, + 256, 24266, 256, 55400, 57234, 256, 24318, 256, 55368, 57137, 256, 55368, + 57137, 256, 33281, 256, 24354, 256, 24354, 256, 14535, 256, 55372, 57016, + 256, 55384, 56794, 256, 24418, 256, 24427, 256, 14563, 256, 24474, 256, + 24525, 256, 24535, 256, 24569, 256, 24705, 256, 14650, 256, 14620, 256, + 24724, 256, 55369, 57044, 256, 24775, 256, 24904, 256, 24908, 256, 24910, + 256, 24908, 256, 24954, 256, 24974, 256, 25010, 256, 24996, 256, 25007, + 256, 25054, 256, 25074, 256, 25078, 256, 25104, 256, 25115, 256, 25181, + 256, 25265, 256, 25300, 256, 25424, 256, 55370, 57100, 256, 25405, 256, + 25340, 256, 25448, 256, 25475, 256, 25572, 256, 55370, 57329, 256, 25634, + 256, 25541, 256, 25513, 256, 14894, 256, 25705, 256, 25726, 256, 25757, + 256, 25719, 256, 14956, 256, 25935, 256, 25964, 256, 55372, 56330, 256, + 26083, 256, 26360, 256, 26185, 256, 15129, 256, 26257, 256, 15112, 256, + 15076, 256, 20882, 256, 20885, 256, 26368, 256, 26268, 256, 32941, 256, + 17369, 256, 26391, 256, 26395, 256, 26401, 256, 26462, 256, 26451, 256, + 55372, 57283, 256, 15177, 256, 26618, 256, 26501, 256, 26706, 256, 26757, + 256, 55373, 56429, 256, 26766, 256, 26655, 256, 26900, 256, 15261, 256, + 26946, 256, 27043, 256, 27114, 256, 27304, 256, 55373, 56995, 256, 27355, + 256, 15384, 256, 27425, 256, 55374, 56487, 256, 27476, 256, 15438, 256, + 27506, 256, 27551, 256, 27578, 256, 27579, 256, 55374, 56973, 256, 55367, + 56587, 256, 55374, 57082, 256, 27726, 256, 55375, 56508, 256, 27839, 256, + 27853, 256, 27751, 256, 27926, 256, 27966, 256, 28023, 256, 27969, 256, + 28009, 256, 28024, 256, 28037, 256, 55375, 56606, 256, 27956, 256, 28207, + 256, 28270, 256, 15667, 256, 28363, 256, 28359, 256, 55375, 57041, 256, + 28153, 256, 28526, 256, 55375, 57182, 256, 55375, 57230, 256, 28614, 256, + 28729, 256, 28702, 256, 28699, 256, 15766, 256, 28746, 256, 28797, 256, + 28791, 256, 28845, 256, 55361, 56613, 256, 28997, 256, 55376, 56931, 256, + 29084, 256, 55376, 57259, 256, 29224, 256, 29237, 256, 29264, 256, 55377, + 56840, 256, 29312, 256, 29333, 256, 55377, 57141, 256, 55378, 56340, 256, + 29562, 256, 29579, 256, 16044, 256, 29605, 256, 16056, 256, 16056, 256, + 29767, 256, 29788, 256, 29809, 256, 29829, 256, 29898, 256, 16155, 256, + 29988, 256, 55379, 56374, 256, 30014, 256, 55379, 56466, 256, 30064, 256, + 55368, 56735, 256, 30224, 256, 55379, 57249, 256, 55379, 57272, 256, + 55380, 56388, 256, 16380, 256, 16392, 256, 30452, 256, 55380, 56563, 256, + 55380, 56562, 256, 55380, 56601, 256, 55380, 56627, 256, 30494, 256, + 30495, 256, 30495, 256, 30538, 256, 16441, 256, 30603, 256, 16454, 256, + 16534, 256, 55381, 56349, 256, 30798, 256, 30860, 256, 30924, 256, 16611, + 256, 55381, 56870, 256, 31062, 256, 55381, 56986, 256, 55381, 57029, 256, + 31119, 256, 31211, 256, 16687, 256, 31296, 256, 31306, 256, 31311, 256, + 55382, 56700, 256, 55382, 56999, 256, 55382, 56999, 256, 31470, 256, + 16898, 256, 55382, 57259, 256, 31686, 256, 31689, 256, 16935, 256, 55383, + 56448, 256, 31954, 256, 17056, 256, 31976, 256, 31971, 256, 32000, 256, + 55383, 57222, 256, 32099, 256, 17153, 256, 32199, 256, 32258, 256, 32325, + 256, 17204, 256, 55384, 56872, 256, 55384, 56903, 256, 17241, 256, 55384, + 57049, 256, 32634, 256, 55384, 57150, 256, 32661, 256, 32762, 256, 32773, + 256, 55385, 56538, 256, 55385, 56611, 256, 32864, 256, 55385, 56744, 256, + 32880, 256, 55372, 57183, 256, 17365, 256, 32946, 256, 33027, 256, 17419, + 256, 33086, 256, 23221, 256, 55385, 57255, 256, 55385, 57269, 256, 55372, + 57235, 256, 55372, 57244, 256, 33281, 256, 33284, 256, 36766, 256, 17515, + 256, 33425, 256, 33419, 256, 33437, 256, 21171, 256, 33457, 256, 33459, + 256, 33469, 256, 33510, 256, 55386, 57148, 256, 33509, 256, 33565, 256, + 33635, 256, 33709, 256, 33571, 256, 33725, 256, 33767, 256, 33879, 256, + 33619, 256, 33738, 256, 33740, 256, 33756, 256, 55387, 56374, 256, 55387, + 56683, 256, 55387, 56533, 256, 17707, 256, 34033, 256, 34035, 256, 34070, + 256, 55388, 57290, 256, 34148, 256, 55387, 57132, 256, 17757, 256, 17761, + 256, 55387, 57265, 256, 55388, 56530, 256, 17771, 256, 34384, 256, 34396, + 256, 34407, 256, 34409, 256, 34473, 256, 34440, 256, 34574, 256, 34530, + 256, 34681, 256, 34600, 256, 34667, 256, 34694, 256, 17879, 256, 34785, + 256, 34817, 256, 17913, 256, 34912, 256, 34915, 256, 55389, 56935, 256, + 35031, 256, 35038, 256, 17973, 256, 35066, 256, 13499, 256, 55390, 56494, + 256, 55390, 56678, 256, 18110, 256, 18119, 256, 35488, 256, 35565, 256, + 35722, 256, 35925, 256, 55391, 56488, 256, 36011, 256, 36033, 256, 36123, + 256, 36215, 256, 55391, 57135, 256, 55362, 56324, 256, 36299, 256, 36284, + 256, 36336, 256, 55362, 56542, 256, 36564, 256, 36664, 256, 55393, 56786, + 256, 55393, 56813, 256, 37012, 256, 37105, 256, 37137, 256, 55393, 57134, + 256, 37147, 256, 37432, 256, 37591, 256, 37592, 256, 37500, 256, 37881, + 256, 37909, 256, 55394, 57338, 256, 38283, 256, 18837, 256, 38327, 256, + 55395, 56695, 256, 18918, 256, 38595, 256, 23986, 256, 38691, 256, 55396, + 56645, 256, 55396, 56858, 256, 19054, 256, 19062, 256, 38880, 256, 55397, + 56330, 256, 19122, 256, 55397, 56470, 256, 38923, 256, 38923, 256, 38953, + 256, 55397, 56758, 256, 39138, 256, 19251, 256, 39209, 256, 39335, 256, + 39362, 256, 39422, 256, 19406, 256, 55398, 57136, 256, 39698, 256, 40000, + 256, 40189, 256, 19662, 256, 19693, 256, 40295, 256, 55400, 56526, 256, + 19704, 256, 55400, 56581, 256, 55400, 56846, 256, 55400, 56977, 256, + 40635, 256, 19798, 256, 40697, 256, 40702, 256, 40709, 256, 40719, 256, + 40726, 256, 40763, 256, 55401, 56832, }; /* index tables for the decomposition data */ @@ -5304,70 +5364,70 @@ static const unsigned short decomp_index2[] = { 13005, 13007, 13009, 13011, 13013, 13015, 13017, 13019, 13021, 13023, 13025, 13027, 13029, 13031, 13033, 13035, 13037, 13039, 13041, 13043, 13045, 13047, 13049, 13051, 13053, 13055, 13057, 13060, 13063, 13066, - 13069, 13073, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 13076, 13079, 0, 0, 0, 0, - 13082, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 13085, 13088, 13091, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 13093, 13095, 13097, 13099, 13101, - 13103, 13105, 13107, 13109, 13111, 13113, 13115, 13117, 13119, 13121, - 13123, 13125, 13127, 13129, 13131, 13133, 13135, 13137, 13139, 13141, - 13143, 13145, 13147, 13149, 13151, 13153, 13155, 13157, 13159, 13161, - 13163, 13165, 13167, 13169, 13171, 13173, 13175, 13177, 13179, 0, 0, 0, - 0, 13181, 13185, 13189, 13193, 13197, 13201, 13205, 13209, 13213, 0, 0, - 0, 0, 0, 0, 0, 13217, 13219, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 13221, 13223, 13225, 13227, 13230, 13232, 13234, 13236, 13238, 13240, - 13242, 13244, 13246, 13248, 13251, 13253, 13255, 13257, 13259, 13262, - 13264, 13266, 13268, 13271, 13273, 13275, 13277, 13279, 13281, 13284, - 13286, 13288, 13290, 13292, 13294, 13296, 13298, 13300, 13302, 13304, - 13306, 13308, 13310, 13312, 13314, 13316, 13318, 13320, 13322, 13324, - 13326, 13328, 13330, 13333, 13335, 13337, 13339, 13342, 13344, 13346, - 13348, 13350, 13352, 13354, 13356, 13358, 13360, 13362, 13364, 13366, - 13368, 13370, 13372, 13374, 13376, 13378, 13380, 13382, 13384, 13386, - 13388, 13390, 13392, 13394, 13396, 13398, 13400, 13402, 13404, 13406, - 13409, 13411, 13413, 13415, 13417, 13419, 13421, 13424, 13427, 13429, - 13431, 13433, 13435, 13437, 13439, 13441, 13443, 13445, 13447, 13450, - 13452, 13454, 13456, 13458, 13461, 13463, 13465, 13467, 13469, 13471, - 13473, 13475, 13477, 13479, 13482, 13484, 13487, 13489, 13491, 13493, - 13495, 13497, 13499, 13501, 13503, 13505, 13507, 13509, 13512, 13514, - 13516, 13518, 13520, 13522, 13525, 13527, 13530, 13533, 13535, 13537, - 13539, 13541, 13544, 13547, 13549, 13551, 13553, 13555, 13557, 13559, - 13561, 13563, 13565, 13567, 13569, 13572, 13574, 13576, 13578, 13580, - 13582, 13584, 13586, 13588, 13590, 13592, 13594, 13596, 13598, 13600, - 13602, 13604, 13606, 13608, 13610, 13613, 13615, 13617, 13619, 13621, - 13623, 13626, 13628, 13630, 13632, 13634, 13636, 13638, 13640, 13642, - 13644, 13646, 13648, 13651, 13653, 13655, 13657, 13659, 13661, 13663, - 13665, 13667, 13669, 13671, 13673, 13675, 13677, 13679, 13681, 13683, - 13685, 13687, 13690, 13692, 13694, 13696, 13698, 13700, 13703, 13705, - 13707, 13709, 13711, 13713, 13715, 13717, 13719, 13722, 13724, 13726, - 13728, 13731, 13733, 13735, 13737, 13739, 13741, 13743, 13746, 13749, - 13752, 13754, 13757, 13759, 13761, 13763, 13765, 13767, 13769, 13771, - 13773, 13775, 13777, 13780, 13782, 13784, 13786, 13788, 13790, 13792, - 13795, 13797, 13799, 13802, 13805, 13807, 13809, 13811, 13813, 13815, - 13817, 13819, 13821, 13823, 13826, 13828, 13831, 13833, 13836, 13838, - 13840, 13842, 13845, 13847, 13849, 13852, 13855, 13857, 13859, 13861, - 13863, 13865, 13867, 13869, 13871, 13873, 13875, 13877, 13879, 13881, - 13884, 13886, 13889, 13891, 13894, 13896, 13899, 13902, 13905, 13907, - 13909, 13911, 13914, 13917, 13920, 13923, 13925, 13927, 13929, 13931, - 13933, 13935, 13937, 13939, 13942, 13944, 13946, 13948, 13950, 13953, - 13955, 13958, 13961, 13963, 13965, 13967, 13969, 13971, 13973, 13976, - 13979, 13982, 13984, 13986, 13989, 13991, 13993, 13995, 13998, 14000, - 14002, 14004, 14006, 14008, 14011, 14013, 14015, 14017, 14019, 14021, - 14023, 14026, 14029, 14031, 14034, 14036, 14039, 14041, 14043, 14045, - 14048, 14051, 14053, 14056, 14058, 14061, 14063, 14065, 14067, 14069, - 14071, 14073, 14076, 14079, 14082, 14085, 14087, 14089, 14091, 14093, - 14095, 14097, 14099, 14101, 14103, 14105, 14107, 14109, 14112, 14114, - 14116, 14118, 14120, 14122, 14124, 14126, 14128, 14130, 14132, 14134, - 14136, 14139, 14142, 14145, 14147, 14149, 14151, 14153, 14156, 14158, - 14161, 14163, 14165, 14168, 14171, 14173, 14175, 14177, 14179, 14181, - 14183, 14185, 14187, 14189, 14191, 14193, 14195, 14197, 14199, 14201, - 14203, 14205, 14207, 14209, 14212, 14214, 14216, 14218, 14220, 14222, - 14225, 14228, 14230, 14232, 14234, 14236, 14238, 14240, 14243, 14245, - 14247, 14249, 14251, 14254, 14257, 14259, 14261, 14263, 14266, 14268, - 14270, 14273, 14276, 14278, 14280, 14282, 14285, 14287, 14289, 14291, - 14293, 14295, 14297, 14299, 14302, 14304, 14306, 14308, 14311, 14313, - 14315, 14317, 14319, 14322, 14325, 14327, 14329, 14331, 14334, 14336, - 14339, 14341, 14343, 14345, 14348, 14350, 14352, 14354, 14356, 14358, - 14360, 14362, 14365, 14367, 14369, 14371, 14373, 14375, 14377, 14380, - 14382, 14385, 14388, 14391, 14393, 14395, 14397, 14399, 14401, 14403, - 14405, 14407, 0, 0, + 13069, 13073, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 13076, 13079, 13082, 0, 0, 0, + 13085, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 13088, 13091, 13094, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 13096, 13098, 13100, 13102, 13104, + 13106, 13108, 13110, 13112, 13114, 13116, 13118, 13120, 13122, 13124, + 13126, 13128, 13130, 13132, 13134, 13136, 13138, 13140, 13142, 13144, + 13146, 13148, 13150, 13152, 13154, 13156, 13158, 13160, 13162, 13164, + 13166, 13168, 13170, 13172, 13174, 13176, 13178, 13180, 13182, 0, 0, 0, + 0, 13184, 13188, 13192, 13196, 13200, 13204, 13208, 13212, 13216, 0, 0, + 0, 0, 0, 0, 0, 13220, 13222, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 13224, 13226, 13228, 13230, 13233, 13235, 13237, 13239, 13241, 13243, + 13245, 13247, 13249, 13251, 13254, 13256, 13258, 13260, 13262, 13265, + 13267, 13269, 13271, 13274, 13276, 13278, 13280, 13282, 13284, 13287, + 13289, 13291, 13293, 13295, 13297, 13299, 13301, 13303, 13305, 13307, + 13309, 13311, 13313, 13315, 13317, 13319, 13321, 13323, 13325, 13327, + 13329, 13331, 13333, 13336, 13338, 13340, 13342, 13345, 13347, 13349, + 13351, 13353, 13355, 13357, 13359, 13361, 13363, 13365, 13367, 13369, + 13371, 13373, 13375, 13377, 13379, 13381, 13383, 13385, 13387, 13389, + 13391, 13393, 13395, 13397, 13399, 13401, 13403, 13405, 13407, 13409, + 13412, 13414, 13416, 13418, 13420, 13422, 13424, 13427, 13430, 13432, + 13434, 13436, 13438, 13440, 13442, 13444, 13446, 13448, 13450, 13453, + 13455, 13457, 13459, 13461, 13464, 13466, 13468, 13470, 13472, 13474, + 13476, 13478, 13480, 13482, 13485, 13487, 13490, 13492, 13494, 13496, + 13498, 13500, 13502, 13504, 13506, 13508, 13510, 13512, 13515, 13517, + 13519, 13521, 13523, 13525, 13528, 13530, 13533, 13536, 13538, 13540, + 13542, 13544, 13547, 13550, 13552, 13554, 13556, 13558, 13560, 13562, + 13564, 13566, 13568, 13570, 13572, 13575, 13577, 13579, 13581, 13583, + 13585, 13587, 13589, 13591, 13593, 13595, 13597, 13599, 13601, 13603, + 13605, 13607, 13609, 13611, 13613, 13616, 13618, 13620, 13622, 13624, + 13626, 13629, 13631, 13633, 13635, 13637, 13639, 13641, 13643, 13645, + 13647, 13649, 13651, 13654, 13656, 13658, 13660, 13662, 13664, 13666, + 13668, 13670, 13672, 13674, 13676, 13678, 13680, 13682, 13684, 13686, + 13688, 13690, 13693, 13695, 13697, 13699, 13701, 13703, 13706, 13708, + 13710, 13712, 13714, 13716, 13718, 13720, 13722, 13725, 13727, 13729, + 13731, 13734, 13736, 13738, 13740, 13742, 13744, 13746, 13749, 13752, + 13755, 13757, 13760, 13762, 13764, 13766, 13768, 13770, 13772, 13774, + 13776, 13778, 13780, 13783, 13785, 13787, 13789, 13791, 13793, 13795, + 13798, 13800, 13802, 13805, 13808, 13810, 13812, 13814, 13816, 13818, + 13820, 13822, 13824, 13826, 13829, 13831, 13834, 13836, 13839, 13841, + 13843, 13845, 13848, 13850, 13852, 13855, 13858, 13860, 13862, 13864, + 13866, 13868, 13870, 13872, 13874, 13876, 13878, 13880, 13882, 13884, + 13887, 13889, 13892, 13894, 13897, 13899, 13902, 13905, 13908, 13910, + 13912, 13914, 13917, 13920, 13923, 13926, 13928, 13930, 13932, 13934, + 13936, 13938, 13940, 13942, 13945, 13947, 13949, 13951, 13953, 13956, + 13958, 13961, 13964, 13966, 13968, 13970, 13972, 13974, 13976, 13979, + 13982, 13985, 13987, 13989, 13992, 13994, 13996, 13998, 14001, 14003, + 14005, 14007, 14009, 14011, 14014, 14016, 14018, 14020, 14022, 14024, + 14026, 14029, 14032, 14034, 14037, 14039, 14042, 14044, 14046, 14048, + 14051, 14054, 14056, 14059, 14061, 14064, 14066, 14068, 14070, 14072, + 14074, 14076, 14079, 14082, 14085, 14088, 14090, 14092, 14094, 14096, + 14098, 14100, 14102, 14104, 14106, 14108, 14110, 14112, 14115, 14117, + 14119, 14121, 14123, 14125, 14127, 14129, 14131, 14133, 14135, 14137, + 14139, 14142, 14145, 14148, 14150, 14152, 14154, 14156, 14159, 14161, + 14164, 14166, 14168, 14171, 14174, 14176, 14178, 14180, 14182, 14184, + 14186, 14188, 14190, 14192, 14194, 14196, 14198, 14200, 14202, 14204, + 14206, 14208, 14210, 14212, 14215, 14217, 14219, 14221, 14223, 14225, + 14228, 14231, 14233, 14235, 14237, 14239, 14241, 14243, 14246, 14248, + 14250, 14252, 14254, 14257, 14260, 14262, 14264, 14266, 14269, 14271, + 14273, 14276, 14279, 14281, 14283, 14285, 14288, 14290, 14292, 14294, + 14296, 14298, 14300, 14302, 14305, 14307, 14309, 14311, 14314, 14316, + 14318, 14320, 14322, 14325, 14328, 14330, 14332, 14334, 14337, 14339, + 14342, 14344, 14346, 14348, 14351, 14353, 14355, 14357, 14359, 14361, + 14363, 14365, 14368, 14370, 14372, 14374, 14376, 14378, 14380, 14383, + 14385, 14388, 14391, 14394, 14396, 14398, 14400, 14402, 14404, 14406, + 14408, 14410, 0, 0, }; /* NFC pairs */ diff --git a/src/hb-unicode-emoji-table.hh b/src/hb-unicode-emoji-table.hh index 1dd0b3211..aa297fcf9 100644 --- a/src/hb-unicode-emoji-table.hh +++ b/src/hb-unicode-emoji-table.hh @@ -7,13 +7,13 @@ * on file with this header: * * # emoji-data.txt - * # Date: 2018-02-07, 07:55:18 GMT - * # © 2018 Unicode®, Inc. + * # Date: 2019-01-15, 12:10:05 GMT + * # © 2019 Unicode®, Inc. * # Unicode and the Unicode Logo are registered trademarks of Unicode, Inc. in the U.S. and other countries. * # For terms of use, see http://www.unicode.org/terms_of_use.html * # * # Emoji Data for UTS #51 - * # Version: 11.0 + * # Version: 12.0 * # * # For documentation and usage, see http://www.unicode.org/reports/tr51 */ From 8b6eb6cf465032d0ca747f4b75f6e9155082bc45 Mon Sep 17 00:00:00 2001 From: Ebrahim Byagowi Date: Fri, 8 Mar 2019 01:33:41 +0330 Subject: [PATCH 024/101] Add a macOS 10.14.3 fonts tests (#1608) --- .circleci/config.yml | 11 +++++++++++ test/shaping/data/in-house/tests/macos.tests | 19 +++++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/.circleci/config.yml b/.circleci/config.yml index e3f8067da..8aa91b21a 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -22,6 +22,16 @@ jobs: - run: make -j4 - run: make check || .ci/fail.sh + macos-10.14.3-aat-fonts: + macos: + xcode: "10.2.0" + steps: + - checkout + - run: HOMEBREW_NO_AUTO_UPDATE=1 brew install wget autoconf automake libtool pkg-config ragel freetype glib cairo + - run: ./autogen.sh --with-freetype --with-glib --with-gobject --with-cairo + - run: make -j4 + - run: make check || .ci/fail.sh + macos-notest-ios: macos: xcode: "10.0.0" @@ -308,6 +318,7 @@ workflows: # macOS - macos-10.12.6-aat-fonts - macos-10.13.6-aat-fonts + - macos-10.14.3-aat-fonts - macos-notest-ios # both autotools and cmake diff --git a/test/shaping/data/in-house/tests/macos.tests b/test/shaping/data/in-house/tests/macos.tests index 7855c0d51..db4a31d57 100644 --- a/test/shaping/data/in-house/tests/macos.tests +++ b/test/shaping/data/in-house/tests/macos.tests @@ -35,3 +35,22 @@ /System/Library/Fonts/SFNSDisplay.ttf@c8948f464ff822a5f9bbf2e12d0e4e32268815aa:--font-ptem 9 --font-funcs ot:U+0054,U+0065,U+0020,U+0041,U+0056,U+0020,U+0054,U+0072,U+0020,U+0056,U+0061,U+0020,U+0072,U+0054,U+0020,U+0065,U+0054,U+0020,U+0054,U+0064:[gid282=0@46,0+1147|gid658=1@-89,0+1006|gid3=2@46,0+512|gid4=3@46,0+1319|gid332=4@-19,0+1319|gid3=5@46,0+512|gid282=6@46,0+1167|gid813=7@-69,0+608|gid3=8@46,0+512|gid332=9@46,0+1309|gid572=10@-29,0+1045|gid3=11@46,0+512|gid813=12@46,0+638|gid282=13@-39,0+1197|gid3=14@46,0+512|gid658=15@46,0+1006|gid282=16@-89,0+1147|gid3=17@46,0+512|gid282=18@46,0+1147|gid649=19@-89,0+1091] /System/Library/Fonts/Apple Color Emoji.ttc@2e09b1f3d42c3821cc6c4ac5b6ce16237ab0d496:--remove-default-ignorables --font-funcs ot:U+1F468,U+200D,U+1F469,U+200D,U+1F467,U+200D,U+1F466:[u1F46A.MWGB=0+800] /Library/Fonts/Zapfino.ttf@99a1e15163c3e9567d5b1019c45e9254dae63b08:--font-funcs ot:U+005A,U+0061,U+0070,U+0066,U+0069,U+006E,U+005A,U+0061,U+0070,U+0066,U+0069,U+006E,U+005A,U+0061,U+0070,U+0066,U+0069,U+006E,U+005A,U+0061,U+0070,U+0066,U+0069,U+006E,U+005A,U+0061,U+0070,U+0066,U+0069,U+006E,U+005A,U+0061,U+0070,U+0066,U+0069,U+006E,U+005A,U+0061,U+0070,U+0066,U+0069,U+006E,U+005A,U+0061,U+0070,U+0066,U+0069,U+006E,U+005A,U+0061,U+0070,U+0066,U+0069,U+006E,U+005A,U+0061,U+0070,U+0066,U+0069,U+006E,U+005A,U+0061,U+0070,U+0066,U+0069,U+006E,U+005A,U+0061,U+0070,U+0066,U+0069,U+006E,U+006F:[Z=0+416|a=1@-21,0+264|p_f=2+433|i=4+181|n=5+261|Z=6+416|a=7@-21,0+264|p_f=8+433|i=10+181|n=11+261|Z=12+416|a=13@-21,0+264|p_f=14+433|i=16+181|n=17+261|Z=18+416|a=19@-21,0+264|p_f=20+433|i=22+181|n=23+261|Z=24+416|a=25@-21,0+264|p_f=26+433|i=28+181|n=29+261|Z=30+416|a=31@-21,0+264|p_f=32+433|i=34+181|n=35+261|Z=36+416|a=37@-21,0+264|p_f=38+433|i=40+181|n=41+261|Z=42+416|a=43@-21,0+264|p_f=44+433|i=46+181|n=47+261|Z=48+416|a=49@-21,0+264|p_f=50+433|i=52+181|n=53+261|Z=54+416|a=55@-21,0+264|p_f=56+433|i=58+181|n=59+261|Z=60+416|a=61@-21,0+264|p_f=62+433|i=64+181|n=65+261|Z_a_p_f_i_n_o=66+2333] + +# 10.14.2 https://gist.github.com/ebraminio/4b731a82f11a662b2164622ebb93086a +/System/Library/Fonts/Helvetica.ttc@992d29a0fa4ed91773457c29b661e94843619cde:--font-funcs ot:U+006D,U+0300:[m=0+1706|gravecmb=0@-284,10+0] +/System/Library/Fonts/LucidaGrande.ttc@63ba1b1de4709bd832ca76bd62368dd99fc34269:--font-funcs ot:U+006D,U+0300:[mgrave=0+1912] +/System/Library/Fonts/Times.ttc@ebb050e4fcaaebe9992efbc7b5660b60ba18b518:--font-funcs ot:U+0066,U+0069:[fi=0+1139] +/Library/Fonts/Khmer MN.ttc@37687fe0bd2548e08e29c92a30e476367ae6356b:--font-funcs ot:U+17A2,U+1780,U+17D2,U+179F,U+179A,U+1781,U+17D2,U+1798,U+17C2,U+179A:[km_qa=0+1230|km_ka=1+1230|km_sa.sub=1+620|km_ro=4+712|km_vs_ae=5+726|km_kha=5+1230|km_mo.sub=5+0|km_ro=9+712] +/Library/Fonts/Tamil MN.ttc@e1df5e056be08937fd65990efbafff0814c03677:--font-funcs ot:U+0BA4,U+0BCA,U+0B95,U+0BC1,U+0B95,U+0BCD,U+0B95,U+0BAA,U+0BCD,U+0BAA,U+0B9F,U+0BCD,U+0B9F,U+0BC1:[tgm_e=0+1702|tgc_ta=0+1598|tgm_aa=0+1149|tgc_ku=2+1962|tgc_k=4+1592|tgc_ka=6+1592|tgc_p=7+1370|tgc_pa=9+1370|tgc_tt=10+1596|tgc_ttu=12+1833] +/System/Library/Fonts/Times.ttc@ebb050e4fcaaebe9992efbc7b5660b60ba18b518:--font-funcs ot:U+0041,U+0066,U+0300,U+0066,U+0069,U+005A:[A=0+1479|f=1+682|gravecmb=1@-551,588+0|fi=3+1139|Z=5+1251] +/System/Library/Fonts/LucidaGrande.ttc@63ba1b1de4709bd832ca76bd62368dd99fc34269:--font-funcs ot:U+05E1,U+05B0:[shevahebrew=0@51,0+0|samekhhebrew=0+1361] +/Library/Fonts/Apple Chancery.ttf@4ec49cba0d4e68d025ada0498c4df1b2f9fd57ac:--font-funcs ot:U+0054,U+0068,U+0020,U+0074,U+0068,U+0020,U+006C,U+006C,U+0020,U+0074,U+0065,U+0020,U+0074,U+006F,U+0020,U+0074,U+0072,U+0020,U+0066,U+0072,U+0020,U+0066,U+0075,U+0020,U+0066,U+006A:[T_h=0+2308|space=2+569|t_h=3+1687|space=5+569|l_l=6+1108|space=8+569|t_e=9+1408|space=11+569|t_o=12+1531|space=14+569|t_r=15+1385|space=17+569|f_r=18+1432|space=20+569|f_u=21+1733|space=23+569|f_j=24+1098] +/Library/Fonts/Apple Chancery.ttf@4ec49cba0d4e68d025ada0498c4df1b2f9fd57ac:--font-funcs ot:U+0054,U+0065,U+0020,U+0041,U+0056,U+0020,U+0054,U+0072,U+0020,U+0056,U+0061,U+0020,U+0072,U+0054,U+0020,U+0065,U+0054,U+0020,U+0054,U+0064:[T=0+1497|e=1@-62,0+699|space=2+569|A=3+1431|V=4@-37,0+1377|space=5+569|T=6+1510|r=7@-50,0+803|space=8+569|V=9+1376|a=10@-37,0+1014|space=11+569|r=12+853|T=13+1560|space=14+569|e=15+761|T=16+1560|space=17+569|T=18+1515|d=19@-45,0+1006] +/System/Library/Fonts/GeezaPro.ttc@ab26ea45dcaa5e1c5a958e42af10e10d330e7334:--font-funcs ot:U+0627,U+0644,U+0623,U+064E,U+0628,U+0652,U+062C,U+064E,U+062F,U+0650,U+064A,U+064E,U+0651,U+0629,U+0640,U+0627,U+0644,U+0639,U+064E,U+0631,U+064E,U+0628,U+0650,U+064A,U+064E,U+0651,U+0629:[u0629.final.tehMarbuta=26+713|u064e_u0651.shaddaFatha=23@0,-200+0|u064a.medial.yeh=23+656|u0650.kasra=21@80,290+80|u0628.initial.beh=21@-80,0+576|u064e.fatha=19@200,-570+200|u0631.final.reh=19@-200,0+702|u064e.fatha=17@200,-200+200|u0639.medial.ain=17@-200,0+738|u0644.initial.lam=16+515|u0627.final.alef=15+647|u0640.tatweel=14+449|u0629.final.tehMarbuta=13+713|u064e_u0651.shaddaFatha=10@0,-200+0|u064a.initial.yeh=10+656|u0650.kasra=8@80,570+80|u062f.final.dal=8@-80,0+822|u064e.fatha=6@290,-160+290|u062c.medial.jeem=6@-290,0+1069|u0652.sukun=4@0,-200+0|u0628.initial.beh=4+656|u064e.fatha=1@-252,120+-252|u0644_u0623.isolated.lamHamzaOnAlef=1@120,0+1282|u0627.alef=0+647] +/System/Library/Fonts/GeezaPro.ttc@ab26ea45dcaa5e1c5a958e42af10e10d330e7334:--font-funcs ot:U+0628,U+064A,U+064E,U+0651,U+0629:[u0629.final.tehMarbuta=4+713|u064e_u0651.shaddaFatha=1@0,-200+0|u064a.medial.yeh=1+656|u0628.initial.beh=0+656] +/System/Library/Fonts/GeezaPro.ttc@ab26ea45dcaa5e1c5a958e42af10e10d330e7334:--font-funcs ot:U+0631,U+0628:[u0628.beh=1+1415|u0631.reh=0@-202,0+700] +/System/Library/Fonts/GeezaPro.ttc@ab26ea45dcaa5e1c5a958e42af10e10d330e7334:--font-funcs ot:U+0628,U+064F:[u064f.damma=0@250,-250+250|u0628.beh=0@-250,0+1165] +/System/Library/Fonts/SFNSDisplay.ttf@6e9677c443f6583228a63fd147663cfc635924d9:--font-funcs ot:U+0054,U+0065,U+0020,U+0041,U+0056,U+0020,U+0054,U+0072,U+0020,U+0056,U+0061,U+0020,U+0072,U+0054,U+0020,U+0065,U+0054,U+0020,U+0054,U+0064:[gid283=0+1055|gid659=1@-135,0+914|gid3=2+420|gid4=3+1227|gid333=4@-65,0+1227|gid3=5+420|gid283=6+1075|gid815=7@-115,0+516|gid3=8+420|gid333=9+1217|gid573=10@-75,0+953|gid3=11+420|gid815=12+546|gid283=13@-85,0+1105|gid3=14+420|gid659=15+914|gid283=16@-135,0+1055|gid3=17+420|gid283=18+1055|gid650=19@-135,0+999] +/System/Library/Fonts/SFNSDisplay.ttf@6e9677c443f6583228a63fd147663cfc635924d9:--font-ptem 9 --font-funcs ot:U+0054,U+0065,U+0020,U+0041,U+0056,U+0020,U+0054,U+0072,U+0020,U+0056,U+0061,U+0020,U+0072,U+0054,U+0020,U+0065,U+0054,U+0020,U+0054,U+0064:[gid283=0@46,0+1147|gid659=1@-89,0+1006|gid3=2@46,0+512|gid4=3@46,0+1319|gid333=4@-19,0+1319|gid3=5@46,0+512|gid283=6@46,0+1167|gid815=7@-69,0+608|gid3=8@46,0+512|gid333=9@46,0+1309|gid573=10@-29,0+1045|gid3=11@46,0+512|gid815=12@46,0+638|gid283=13@-39,0+1197|gid3=14@46,0+512|gid659=15@46,0+1006|gid283=16@-89,0+1147|gid3=17@46,0+512|gid283=18@46,0+1147|gid650=19@-89,0+1091] +/System/Library/Fonts/Apple Color Emoji.ttc@60f77161021b1b87e99c3690e1a9b56341cf8792:--remove-default-ignorables --font-funcs ot:U+1F468,U+200D,U+1F469,U+200D,U+1F467,U+200D,U+1F466:[u1F46A.MWGB=0+800] +/Library/Fonts/Zapfino.ttf@99a1e15163c3e9567d5b1019c45e9254dae63b08:--font-funcs ot:U+005A,U+0061,U+0070,U+0066,U+0069,U+006E,U+005A,U+0061,U+0070,U+0066,U+0069,U+006E,U+005A,U+0061,U+0070,U+0066,U+0069,U+006E,U+005A,U+0061,U+0070,U+0066,U+0069,U+006E,U+005A,U+0061,U+0070,U+0066,U+0069,U+006E,U+005A,U+0061,U+0070,U+0066,U+0069,U+006E,U+005A,U+0061,U+0070,U+0066,U+0069,U+006E,U+005A,U+0061,U+0070,U+0066,U+0069,U+006E,U+005A,U+0061,U+0070,U+0066,U+0069,U+006E,U+005A,U+0061,U+0070,U+0066,U+0069,U+006E,U+005A,U+0061,U+0070,U+0066,U+0069,U+006E,U+005A,U+0061,U+0070,U+0066,U+0069,U+006E,U+006F:[Z=0+416|a=1@-21,0+264|p_f=2+433|i=4+181|n=5+261|Z=6+416|a=7@-21,0+264|p_f=8+433|i=10+181|n=11+261|Z=12+416|a=13@-21,0+264|p_f=14+433|i=16+181|n=17+261|Z=18+416|a=19@-21,0+264|p_f=20+433|i=22+181|n=23+261|Z=24+416|a=25@-21,0+264|p_f=26+433|i=28+181|n=29+261|Z=30+416|a=31@-21,0+264|p_f=32+433|i=34+181|n=35+261|Z=36+416|a=37@-21,0+264|p_f=38+433|i=40+181|n=41+261|Z=42+416|a=43@-21,0+264|p_f=44+433|i=46+181|n=47+261|Z=48+416|a=49@-21,0+264|p_f=50+433|i=52+181|n=53+261|Z=54+416|a=55@-21,0+264|p_f=56+433|i=58+181|n=59+261|Z=60+416|a=61@-21,0+264|p_f=62+433|i=64+181|n=65+261|Z_a_p_f_i_n_o=66+2333] From 30d7c40f8ce9f47d733b1f43a93f20739772859e Mon Sep 17 00:00:00 2001 From: Eric Muller Date: Sat, 9 Feb 2019 02:55:27 -0800 Subject: [PATCH 025/101] Add a flag to hb_buffer_t to prevent the insertion of dotted circles on incorrect character sequences. Current behavior unchanged if this flag is not set (and it isn't by default). --- src/hb-buffer.h | 7 ++++++- src/hb-ot-shape-complex-hangul.cc | 3 ++- src/hb-ot-shape-complex-indic.cc | 3 +++ src/hb-ot-shape-complex-khmer.cc | 3 +++ src/hb-ot-shape-complex-myanmar.cc | 3 +++ src/hb-ot-shape-complex-use.cc | 3 +++ src/hb-ot-shape-complex-vowel-constraints.cc | 3 +++ src/hb-ot-shape.cc | 3 +++ 8 files changed, 26 insertions(+), 2 deletions(-) diff --git a/src/hb-buffer.h b/src/hb-buffer.h index f989d25d6..5819e28e0 100644 --- a/src/hb-buffer.h +++ b/src/hb-buffer.h @@ -284,6 +284,10 @@ hb_buffer_guess_segment_properties (hb_buffer_t *buffer); * space glyph and zeroing the advance width.) * @HB_BUFFER_FLAG_PRESERVE_DEFAULT_IGNORABLES takes * precedence over this flag. Since: 1.8.0 + * @HB_BUFFER_FLAG_DO_NOT_INSERT_DOTTED_CIRCLE: + * flag indicating that a dotted circle should + * not be inserted in the rendering of incorrect + * character sequences (such at <0905 093E>). * * Since: 0.9.20 */ @@ -292,7 +296,8 @@ typedef enum { /*< flags >*/ HB_BUFFER_FLAG_BOT = 0x00000001u, /* Beginning-of-text */ HB_BUFFER_FLAG_EOT = 0x00000002u, /* End-of-text */ HB_BUFFER_FLAG_PRESERVE_DEFAULT_IGNORABLES = 0x00000004u, - HB_BUFFER_FLAG_REMOVE_DEFAULT_IGNORABLES = 0x00000008u + HB_BUFFER_FLAG_REMOVE_DEFAULT_IGNORABLES = 0x00000008u, + HB_BUFFER_FLAG_DO_NOT_INSERT_DOTTED_CIRCLE = 0x00000010u } hb_buffer_flags_t; HB_EXTERN void diff --git a/src/hb-ot-shape-complex-hangul.cc b/src/hb-ot-shape-complex-hangul.cc index e143867e1..8e7c4c007 100644 --- a/src/hb-ot-shape-complex-hangul.cc +++ b/src/hb-ot-shape-complex-hangul.cc @@ -214,7 +214,8 @@ preprocess_text_hangul (const hb_ot_shape_plan_t *plan HB_UNUSED, else { /* No valid syllable as base for tone mark; try to insert dotted circle. */ - if (font->has_glyph (0x25CCu)) + if ( !(buffer->flags & HB_BUFFER_FLAG_DO_NOT_INSERT_DOTTED_CIRCLE) + && font->has_glyph (0x25CCu)) { hb_codepoint_t chars[2]; if (!is_zero_width_char (font, u)) { diff --git a/src/hb-ot-shape-complex-indic.cc b/src/hb-ot-shape-complex-indic.cc index 64e7dcff3..1c7bba5e8 100644 --- a/src/hb-ot-shape-complex-indic.cc +++ b/src/hb-ot-shape-complex-indic.cc @@ -965,6 +965,9 @@ insert_dotted_circles (const hb_ot_shape_plan_t *plan HB_UNUSED, hb_font_t *font, hb_buffer_t *buffer) { + if (unlikely (buffer->flags & HB_BUFFER_FLAG_DO_NOT_INSERT_DOTTED_CIRCLE)) + return; + /* Note: This loop is extra overhead, but should not be measurable. * TODO Use a buffer scratch flag to remove the loop. */ bool has_broken_syllables = false; diff --git a/src/hb-ot-shape-complex-khmer.cc b/src/hb-ot-shape-complex-khmer.cc index 4475ceb7c..0bc2309bf 100644 --- a/src/hb-ot-shape-complex-khmer.cc +++ b/src/hb-ot-shape-complex-khmer.cc @@ -365,6 +365,9 @@ insert_dotted_circles (const hb_ot_shape_plan_t *plan HB_UNUSED, hb_font_t *font, hb_buffer_t *buffer) { + if (unlikely (buffer->flags & HB_BUFFER_FLAG_DO_NOT_INSERT_DOTTED_CIRCLE)) + return; + /* Note: This loop is extra overhead, but should not be measurable. */ bool has_broken_syllables = false; unsigned int count = buffer->len; diff --git a/src/hb-ot-shape-complex-myanmar.cc b/src/hb-ot-shape-complex-myanmar.cc index 8fdf2f4bc..70ab972c6 100644 --- a/src/hb-ot-shape-complex-myanmar.cc +++ b/src/hb-ot-shape-complex-myanmar.cc @@ -298,6 +298,9 @@ insert_dotted_circles (const hb_ot_shape_plan_t *plan HB_UNUSED, hb_font_t *font, hb_buffer_t *buffer) { + if (unlikely (buffer->flags & HB_BUFFER_FLAG_DO_NOT_INSERT_DOTTED_CIRCLE)) + return; + /* Note: This loop is extra overhead, but should not be measurable. */ bool has_broken_syllables = false; unsigned int count = buffer->len; diff --git a/src/hb-ot-shape-complex-use.cc b/src/hb-ot-shape-complex-use.cc index 2e3f202f5..eecde6e85 100644 --- a/src/hb-ot-shape-complex-use.cc +++ b/src/hb-ot-shape-complex-use.cc @@ -523,6 +523,9 @@ insert_dotted_circles (const hb_ot_shape_plan_t *plan HB_UNUSED, hb_font_t *font, hb_buffer_t *buffer) { + if (unlikely (buffer->flags & HB_BUFFER_FLAG_DO_NOT_INSERT_DOTTED_CIRCLE)) + return; + /* Note: This loop is extra overhead, but should not be measurable. */ bool has_broken_syllables = false; unsigned int count = buffer->len; diff --git a/src/hb-ot-shape-complex-vowel-constraints.cc b/src/hb-ot-shape-complex-vowel-constraints.cc index 4652d02c1..919c0305b 100644 --- a/src/hb-ot-shape-complex-vowel-constraints.cc +++ b/src/hb-ot-shape-complex-vowel-constraints.cc @@ -34,6 +34,9 @@ _hb_preprocess_text_vowel_constraints (const hb_ot_shape_plan_t *plan HB_UNUSED, hb_buffer_t *buffer, hb_font_t *font HB_UNUSED) { + if (unlikely (buffer->flags & HB_BUFFER_FLAG_DO_NOT_INSERT_DOTTED_CIRCLE)) + return; + /* UGLY UGLY UGLY business of adding dotted-circle in the middle of * vowel-sequences that look like another vowel. Data for each script * collected from the USE script development spec. diff --git a/src/hb-ot-shape.cc b/src/hb-ot-shape.cc index e9d97c9b0..7fff3059e 100644 --- a/src/hb-ot-shape.cc +++ b/src/hb-ot-shape.cc @@ -448,6 +448,9 @@ hb_set_unicode_props (hb_buffer_t *buffer) static void hb_insert_dotted_circle (hb_buffer_t *buffer, hb_font_t *font) { + if (unlikely (buffer->flags & HB_BUFFER_FLAG_DO_NOT_INSERT_DOTTED_CIRCLE)) + return; + if (!(buffer->flags & HB_BUFFER_FLAG_BOT) || buffer->context_len[0] || !_hb_glyph_info_is_unicode_mark (&buffer->info[0])) From 44a67ddeb878f7639b30d1884e38b1525aab4f4a Mon Sep 17 00:00:00 2001 From: Eric Muller Date: Sun, 10 Feb 2019 04:31:41 -0800 Subject: [PATCH 026/101] Fix coding style. --- src/hb-ot-shape-complex-hangul.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/hb-ot-shape-complex-hangul.cc b/src/hb-ot-shape-complex-hangul.cc index 8e7c4c007..f084f6ad6 100644 --- a/src/hb-ot-shape-complex-hangul.cc +++ b/src/hb-ot-shape-complex-hangul.cc @@ -214,8 +214,8 @@ preprocess_text_hangul (const hb_ot_shape_plan_t *plan HB_UNUSED, else { /* No valid syllable as base for tone mark; try to insert dotted circle. */ - if ( !(buffer->flags & HB_BUFFER_FLAG_DO_NOT_INSERT_DOTTED_CIRCLE) - && font->has_glyph (0x25CCu)) + if (!(buffer->flags & HB_BUFFER_FLAG_DO_NOT_INSERT_DOTTED_CIRCLE) && + font->has_glyph (0x25CCu)) { hb_codepoint_t chars[2]; if (!is_zero_width_char (font, u)) { From b38bab86229bc40d9cdf4819d6dc6aab444d0291 Mon Sep 17 00:00:00 2001 From: Eric Muller Date: Tue, 12 Feb 2019 11:41:16 -0800 Subject: [PATCH 027/101] Update generation code for hb-ot-shape-complex-vowel-constraints.cc. Remove 'unlikely' --- src/gen-vowel-constraints.py | 3 +++ src/hb-ot-shape-complex-vowel-constraints.cc | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/gen-vowel-constraints.py b/src/gen-vowel-constraints.py index e4dc95dfe..afb21d928 100755 --- a/src/gen-vowel-constraints.py +++ b/src/gen-vowel-constraints.py @@ -180,6 +180,9 @@ print ('_hb_preprocess_text_vowel_constraints (const hb_ot_shape_plan_t *plan HB print ('\t\t\t\t hb_buffer_t *buffer,') print ('\t\t\t\t hb_font_t *font HB_UNUSED)') print ('{') +print (' if (buffer->flags & HB_BUFFER_FLAG_DO_NOT_INSERT_DOTTED_CIRCLE)') +print (' return;') +print () print (' /* UGLY UGLY UGLY business of adding dotted-circle in the middle of') print (' * vowel-sequences that look like another vowel. Data for each script') print (' * collected from the USE script development spec.') diff --git a/src/hb-ot-shape-complex-vowel-constraints.cc b/src/hb-ot-shape-complex-vowel-constraints.cc index 919c0305b..e4cf64561 100644 --- a/src/hb-ot-shape-complex-vowel-constraints.cc +++ b/src/hb-ot-shape-complex-vowel-constraints.cc @@ -34,7 +34,7 @@ _hb_preprocess_text_vowel_constraints (const hb_ot_shape_plan_t *plan HB_UNUSED, hb_buffer_t *buffer, hb_font_t *font HB_UNUSED) { - if (unlikely (buffer->flags & HB_BUFFER_FLAG_DO_NOT_INSERT_DOTTED_CIRCLE)) + if (buffer->flags & HB_BUFFER_FLAG_DO_NOT_INSERT_DOTTED_CIRCLE) return; /* UGLY UGLY UGLY business of adding dotted-circle in the middle of From 8c42f03215097d7c1bae74db7e98315263d3e8a4 Mon Sep 17 00:00:00 2001 From: David Corbett Date: Fri, 8 Mar 2019 09:46:48 -0500 Subject: [PATCH 028/101] Remove obsolete overrides from Indic/USE scripts --- src/gen-indic-table.py | 4 ---- src/gen-use-table.py | 30 +++++++--------------------- src/hb-ot-shape-complex-use-table.cc | 2 +- 3 files changed, 8 insertions(+), 28 deletions(-) diff --git a/src/gen-indic-table.py b/src/gen-indic-table.py index 6532ee7e0..eedf420b7 100755 --- a/src/gen-indic-table.py +++ b/src/gen-indic-table.py @@ -79,10 +79,6 @@ data = combined del combined num = len (data) -for u in [0x17CD, 0x17CE, 0x17CF, 0x17D0, 0x17D3]: - if data[u][0] == 'Other': - data[u][0] = "Vowel_Dependent" - # Move the outliers NO-BREAK SPACE and DOTTED CIRCLE out singles = {} for u in ALLOWED_SINGLES: diff --git a/src/gen-use-table.py b/src/gen-use-table.py index 2631c46ff..c725bb2c0 100755 --- a/src/gen-use-table.py +++ b/src/gen-use-table.py @@ -48,7 +48,6 @@ defaults = ('Other', 'Not_Applicable', 'Cn', 'No_Block') # TODO Characters that are not in Unicode Indic files, but used in USE data[0][0x034F] = defaults[0] data[0][0x2060] = defaults[0] -data[0][0x20F0] = defaults[0] # TODO https://github.com/roozbehp/unicode-data/issues/9 data[0][0x11C44] = 'Consonant_Placeholder' data[0][0x11C45] = 'Consonant_Placeholder' @@ -317,12 +316,11 @@ def map_to_use(data): # Resolve Indic_Syllabic_Category - # TODO: These don't have UISC assigned in Unicode 8.0, but have UIPC - if U == 0x17DD: UISC = Vowel_Dependent + # TODO: These don't have UISC assigned in Unicode 12.0, but have UIPC if 0x1CE2 <= U <= 0x1CE8: UISC = Cantillation_Mark # Tibetan: - # TODO: These don't have UISC assigned in Unicode 11.0, but have UIPC + # TODO: These don't have UISC assigned in Unicode 12.0, but have UIPC if 0x0F18 <= U <= 0x0F19 or 0x0F3E <= U <= 0x0F3F: UISC = Vowel_Dependent if 0x0F86 <= U <= 0x0F87: UISC = Tone_Mark # Overrides to allow NFC order matching syllable @@ -347,13 +345,7 @@ def map_to_use(data): if U == 0x1CED: UISC = Tone_Mark # TODO: https://github.com/harfbuzz/harfbuzz/issues/525 - if U == 0x1A7F: UISC = Consonant_Final; UIPC = Bottom - - # TODO: https://github.com/harfbuzz/harfbuzz/pull/609 - if U == 0x20F0: UISC = Cantillation_Mark; UIPC = Top - - # TODO: https://github.com/harfbuzz/harfbuzz/pull/626 - if U == 0xA8B4: UISC = Consonant_Medial + if U == 0x1A7F: UISC = Consonant_Final # TODO: https://github.com/harfbuzz/harfbuzz/issues/1105 if U == 0x11134: UISC = Gemination_Mark @@ -367,26 +359,18 @@ def map_to_use(data): # Resolve Indic_Positional_Category - # TODO: Not in Unicode 8.0 yet, but in spec. - if U == 0x1B6C: UIPC = Bottom - - # TODO: These should die, but have UIPC in Unicode 8.0 + # TODO: These should die, but have UIPC in Unicode 12.0 if U in [0x953, 0x954]: UIPC = Not_Applicable - # TODO: In USE's override list but not in Unicode 11.0 + # TODO: In USE's override list but not in Unicode 12.0 if U == 0x103C: UIPC = Left - # TODO: These are not in USE's override list that we have, nor are they in Unicode 11.0 + # TODO: These are not in USE's override list that we have, nor are they in Unicode 12.0 if 0xA926 <= U <= 0xA92A: UIPC = Top - if U == 0x111CA: UIPC = Bottom - if U == 0x11300: UIPC = Top # TODO: https://github.com/harfbuzz/harfbuzz/pull/1037 if U == 0x11302: UIPC = Top - if U == 0x1133C: UIPC = Bottom - if U == 0x1171E: UIPC = Left # Correct?! + if U == 0x1171E: UIPC = Left if 0x1CF8 <= U <= 0x1CF9: UIPC = Top - # https://github.com/roozbehp/unicode-data/issues/8 - if U == 0x0A51: UIPC = Bottom assert (UIPC in [Not_Applicable, Visual_Order_Left] or USE in use_positions), "%s %s %s %s %s" % (hex(U), UIPC, USE, UISC, UGC) diff --git a/src/hb-ot-shape-complex-use-table.cc b/src/hb-ot-shape-complex-use-table.cc index 6c054a626..4de538209 100644 --- a/src/hb-ot-shape-complex-use-table.cc +++ b/src/hb-ot-shape-complex-use-table.cc @@ -261,7 +261,7 @@ static const USE_TABLE_ELEMENT_TYPE use_table[] = { /* 17A0 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, /* 17B0 */ B, B, B, B, O, O, VPst, VAbv, VAbv, VAbv, VAbv, VBlw, VBlw, VBlw, VPst, VPst, /* 17C0 */ VPst, VPre, VPre, VPre, VPst, VPst, VMAbv, VMPst, VPst, VMAbv, VMAbv, FM, FAbv, CMAbv, FM, FM, - /* 17D0 */ FM, VAbv, H, FM, O, O, O, O, O, O, O, O, B, VAbv, O, O, + /* 17D0 */ FM, VAbv, H, FM, O, O, O, O, O, O, O, O, B, FM, O, O, /* 17E0 */ B, B, B, B, B, B, B, B, B, B, O, O, O, O, O, O, #define use_offset_0x1900u 1936 From c2442c90d6ecfaee987ed8ac6f93a9ac6b07c642 Mon Sep 17 00:00:00 2001 From: Khaled Hosny Date: Tue, 12 Mar 2019 01:09:27 +0200 Subject: [PATCH 029/101] [doc] Add placeholder since version for new flag --- src/hb-buffer.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hb-buffer.h b/src/hb-buffer.h index 5819e28e0..43aeb99db 100644 --- a/src/hb-buffer.h +++ b/src/hb-buffer.h @@ -287,7 +287,7 @@ hb_buffer_guess_segment_properties (hb_buffer_t *buffer); * @HB_BUFFER_FLAG_DO_NOT_INSERT_DOTTED_CIRCLE: * flag indicating that a dotted circle should * not be inserted in the rendering of incorrect - * character sequences (such at <0905 093E>). + * character sequences (such at <0905 093E>). Since: REPLACEME * * Since: 0.9.20 */ From e52ec3fc23c2d5a881849f047885e0423bd74740 Mon Sep 17 00:00:00 2001 From: Behdad Esfahbod Date: Mon, 11 Mar 2019 18:09:51 -0700 Subject: [PATCH 030/101] Remove redundant hb_ot_layout_lookup_would_substitute_fast --- src/hb-ot-layout.cc | 13 ------------- src/hb-ot-layout.hh | 7 ------- src/hb-ot-shape-complex-indic.cc | 2 +- src/hb-ot-shape-complex-khmer.cc | 2 +- 4 files changed, 2 insertions(+), 22 deletions(-) diff --git a/src/hb-ot-layout.cc b/src/hb-ot-layout.cc index d32be04a8..1365a3e21 100644 --- a/src/hb-ot-layout.cc +++ b/src/hb-ot-layout.cc @@ -957,19 +957,6 @@ hb_ot_layout_lookup_would_substitute (hb_face_t *face, const hb_codepoint_t *glyphs, unsigned int glyphs_length, hb_bool_t zero_context) -{ - return hb_ot_layout_lookup_would_substitute_fast (face, - lookup_index, - glyphs, glyphs_length, - zero_context); -} - -bool -hb_ot_layout_lookup_would_substitute_fast (hb_face_t *face, - unsigned int lookup_index, - const hb_codepoint_t *glyphs, - unsigned int glyphs_length, - bool zero_context) { if (unlikely (lookup_index >= face->table.GSUB->lookup_count)) return false; OT::hb_would_apply_context_t c (face, glyphs, glyphs_length, (bool) zero_context); diff --git a/src/hb-ot-layout.hh b/src/hb-ot-layout.hh index 5125e9fa9..be7ef0263 100644 --- a/src/hb-ot-layout.hh +++ b/src/hb-ot-layout.hh @@ -96,13 +96,6 @@ HB_MARK_AS_FLAG_T (hb_ot_layout_glyph_props_flags_t); * GSUB/GPOS */ -HB_INTERNAL bool -hb_ot_layout_lookup_would_substitute_fast (hb_face_t *face, - unsigned int lookup_index, - const hb_codepoint_t *glyphs, - unsigned int glyphs_length, - bool zero_context); - /* Should be called before all the substitute_lookup's are done. */ HB_INTERNAL void diff --git a/src/hb-ot-shape-complex-indic.cc b/src/hb-ot-shape-complex-indic.cc index 1c7bba5e8..d421555fb 100644 --- a/src/hb-ot-shape-complex-indic.cc +++ b/src/hb-ot-shape-complex-indic.cc @@ -239,7 +239,7 @@ struct would_substitute_feature_t hb_face_t *face) const { for (unsigned int i = 0; i < count; i++) - if (hb_ot_layout_lookup_would_substitute_fast (face, lookups[i].index, glyphs, glyphs_count, zero_context)) + if (hb_ot_layout_lookup_would_substitute (face, lookups[i].index, glyphs, glyphs_count, zero_context)) return true; return false; } diff --git a/src/hb-ot-shape-complex-khmer.cc b/src/hb-ot-shape-complex-khmer.cc index 0bc2309bf..5746651d6 100644 --- a/src/hb-ot-shape-complex-khmer.cc +++ b/src/hb-ot-shape-complex-khmer.cc @@ -164,7 +164,7 @@ struct would_substitute_feature_t hb_face_t *face) const { for (unsigned int i = 0; i < count; i++) - if (hb_ot_layout_lookup_would_substitute_fast (face, lookups[i].index, glyphs, glyphs_count, zero_context)) + if (hb_ot_layout_lookup_would_substitute (face, lookups[i].index, glyphs, glyphs_count, zero_context)) return true; return false; } From 99502b324dd6cb45d401bc5f6cc08d7a77677ba5 Mon Sep 17 00:00:00 2001 From: Michiharu Ariza Date: Tue, 12 Mar 2019 11:03:53 -0700 Subject: [PATCH 031/101] 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 */ From 8b1eaecd9485fe504af364db1537bb04852b265c Mon Sep 17 00:00:00 2001 From: Khaled Hosny Date: Wed, 13 Mar 2019 13:21:12 +0200 Subject: [PATCH 032/101] [ci] Simplify and fix Travis CI macOS build --- .travis.yml | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/.travis.yml b/.travis.yml index afb1b1ae6..88967ddd4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -46,13 +46,7 @@ matrix: - os: osx compiler: clang install: - - brew update; - # Workaround Travis/brew bug - - brew uninstall libtool && brew install libtool - - brew install ragel freetype glib gobject-introspection cairo graphite2 || true - - brew upgrade icu4c || true - - export PATH="/usr/local/opt/icu4c/sbin:/usr/local/opt/icu4c/bin:$PATH" - - export PKG_CONFIG_PATH="/usr/local/opt/icu4c/lib/pkgconfig" + - brew link --force icu4c script: - ./autogen.sh - ./configure $CONFIGURE_OPTS --with-coretext @@ -76,6 +70,14 @@ addons: - libicu-dev # for extra unicode functions - libgraphite2-dev # for extra shapers #- libgirepository1.0-dev # for gobject-introspection + homebrew: + packages: + - cairo + - freetype + - glib + - graphite2 + - icu4c + #- gobject-introspection coverity_scan: project: From 7de9f92ee9ced6f4c176459cf25f4ca931ca5ceb Mon Sep 17 00:00:00 2001 From: David Corbett Date: Tue, 12 Mar 2019 19:30:47 -0400 Subject: [PATCH 033/101] Categorize U+09FC as Consonant_Placeholder --- src/hb-ot-shape-complex-indic.hh | 1 + 1 file changed, 1 insertion(+) diff --git a/src/hb-ot-shape-complex-indic.hh b/src/hb-ot-shape-complex-indic.hh index dcc2a7a6c..136e34991 100644 --- a/src/hb-ot-shape-complex-indic.hh +++ b/src/hb-ot-shape-complex-indic.hh @@ -362,6 +362,7 @@ set_indic_properties (hb_glyph_info_t &info) else if (unlikely (u == 0x0AFBu)) cat = OT_N; /* https://github.com/harfbuzz/harfbuzz/issues/552 */ else if (unlikely (u == 0x0980u)) cat = OT_PLACEHOLDER; /* https://github.com/harfbuzz/harfbuzz/issues/538 */ + else if (unlikely (u == 0x09FCu)) cat = OT_PLACEHOLDER; /* https://github.com/harfbuzz/harfbuzz/pull/1613 */ else if (unlikely (u == 0x0C80u)) cat = OT_PLACEHOLDER; /* https://github.com/harfbuzz/harfbuzz/pull/623 */ else if (unlikely (hb_in_range (u, 0x2010u, 0x2011u))) cat = OT_PLACEHOLDER; From b1dfb8c850f36d4065190a779a6e3342a5fbb593 Mon Sep 17 00:00:00 2001 From: Khaled Hosny Date: Thu, 14 Mar 2019 21:41:25 +0200 Subject: [PATCH 034/101] [ci] Cache FreeType build on Travis --- .ci/build-freetype.sh | 17 +++++++++++++++++ .travis.yml | 18 ++++++++++++------ 2 files changed, 29 insertions(+), 6 deletions(-) create mode 100644 .ci/build-freetype.sh diff --git a/.ci/build-freetype.sh b/.ci/build-freetype.sh new file mode 100644 index 000000000..0f09f92e9 --- /dev/null +++ b/.ci/build-freetype.sh @@ -0,0 +1,17 @@ +#!/bin/bash + +set -x +set -o errexit -o nounset + +# 22.0.16 is the libtool version of 2.9.0 +if pkg-config --atleast-version 22.0.16 freetype2; then exit; fi + +pushd $HOME +wget http://download.savannah.gnu.org/releases/freetype/freetype-2.9.tar.bz2 +tar xf freetype-2.9.tar.bz2 +pushd freetype-2.9 +./autogen.sh +./configure --prefix=$HOME/.local +make -j4 install +popd +popd diff --git a/.travis.yml b/.travis.yml index 88967ddd4..681471dc0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -16,9 +16,10 @@ matrix: - os: linux compiler: gcc script: - # Remove these two lines when Travis updated its distro - - wget http://download.savannah.gnu.org/releases/freetype/freetype-2.9.tar.bz2 && tar xf freetype-2.9.tar.bz2 && cd freetype-2.9 && ./autogen.sh && ./configure && make -j4 && cd .. - - export LD_LIBRARY_PATH="$PWD/freetype-2.9/objs/.libs" + # Remove the following three lines when Travis updates its distro + - export PKG_CONFIG_PATH="$HOME/.local/lib/pkgconfig" + - export LD_LIBRARY_PATH="$HOME/.local/lib" + - bash .ci/build-freetype.sh - ./autogen.sh - ./configure $CONFIGURE_OPTS --enable-gtk-doc --enable-code-coverage @@ -34,9 +35,10 @@ matrix: - os: linux compiler: clang script: - # Remove these two lines when Travis updated its distro - - wget http://download.savannah.gnu.org/releases/freetype/freetype-2.9.tar.bz2 && tar xf freetype-2.9.tar.bz2 && cd freetype-2.9 && ./autogen.sh && ./configure && make -j4 && cd .. - - export LD_LIBRARY_PATH="$PWD/freetype-2.9/objs/.libs" + # Remove the following three lines when Travis updates its distro + - export PKG_CONFIG_PATH="$HOME/.local/lib/pkgconfig" + - export LD_LIBRARY_PATH="$HOME/.local/lib" + - bash .ci/build-freetype.sh - ./autogen.sh - ./configure $CONFIGURE_OPTS @@ -57,6 +59,10 @@ notifications: irc: "irc.freenode.org#harfbuzz" email: harfbuzz-bots-chatter@googlegroups.com +cache: + directories: + - /home/travis/.local + addons: apt: packages: From 8aaab78efcac81a05ec919be13792c98741ea1b5 Mon Sep 17 00:00:00 2001 From: Ebrahim Byagowi Date: Thu, 14 Mar 2019 16:49:42 -0700 Subject: [PATCH 035/101] Allow zero length ranges in sanitization (#1617) Fixes fvar table sanitization where there are no named instance by allowing zero length ranges starting from Null() address. Fixes #1607 --- src/hb-machinery.hh | 30 ++++++++++++++++-------------- test/api/fonts/Zycon.ttf | Bin 0 -> 21036 bytes test/api/test-ot-face.c | 9 +++++++++ 3 files changed, 25 insertions(+), 14 deletions(-) create mode 100644 test/api/fonts/Zycon.ttf diff --git a/src/hb-machinery.hh b/src/hb-machinery.hh index b22c2389c..ffa423d2f 100644 --- a/src/hb-machinery.hh +++ b/src/hb-machinery.hh @@ -326,27 +326,29 @@ struct hb_sanitize_context_t : } bool check_range (const void *base, - unsigned int len) const + unsigned int len) const { const char *p = (const char *) base; - bool ok = this->start <= p && - p <= this->end && - (unsigned int) (this->end - p) >= len && - this->max_ops-- > 0; + bool ok = !len || + (this->start <= p && + p <= this->end && + (unsigned int) (this->end - p) >= len && + this->max_ops-- > 0); DEBUG_MSG_LEVEL (SANITIZE, p, this->debug_depth+1, 0, - "check_range [%p..%p] (%d bytes) in [%p..%p] -> %s", - p, p + len, len, - this->start, this->end, - ok ? "OK" : "OUT-OF-RANGE"); + "check_range [%p..%p]" + " (%d bytes) in [%p..%p] -> %s", + p, p + len, len, + this->start, this->end, + ok ? "OK" : "OUT-OF-RANGE"); return likely (ok); } template bool check_range (const T *base, - unsigned int a, - unsigned int b) const + unsigned int a, + unsigned int b) const { return !hb_unsigned_mul_overflows (a, b) && this->check_range (base, a * b); @@ -354,9 +356,9 @@ struct hb_sanitize_context_t : template bool check_range (const T *base, - unsigned int a, - unsigned int b, - unsigned int c) const + unsigned int a, + unsigned int b, + unsigned int c) const { return !hb_unsigned_mul_overflows (a, b) && this->check_range (base, a * b, c); diff --git a/test/api/fonts/Zycon.ttf b/test/api/fonts/Zycon.ttf new file mode 100644 index 0000000000000000000000000000000000000000..3a6761b8f7878021758e049c9ec79831a4268c4d GIT binary patch literal 21036 zcmch92Ygh;*7(f5ckh;LcJKDh_LALA&!+9B5jG(SA%qY@h>;RP3qe8=kY18d1VKba z1w}<4Hb5UDPko4r4Y6WFrKofe>768d@BiH0Bm~s=z3=z?e*b%S=AJoo=A0>KX3m|N zyVIuJHwH;j6CzQd|G?zbhvp0`MF^K5gvv)v7%{nA`RjHF9|M_4<6X>sWW#WI?p1_DfI#S_)%_3g&+o4*WE{&a5DSHn zXDBhjW8s1{VM=iwcN!tFFvKYw7xq;ljEY);ibDuKWgr{{J};N&Ab=YSo)n}3$`yW+ z>rp+S?4tY&Air_iRN!UfxNsgs2yfuI5#{9M&~p&rZ-Cna;bM9k#{w)7^hS+>2`Epv zjyo-mKqX=kiUWKPaF2mw!0i*)AtgLN0-*$ymd~Xco)UOgAu5!ADK7|Mn;UtOJj<^OPUXgi8sr;Y%8NoazYiCA4)okmEFj9TFqt1~l*Z+V&)e9g@NG z*|0+jgyN&a4lxK{HisNSa(G@1IfS4^u0X|rLnsoSnvg>%0-lbLL!ia@%5x!ykP_jm zVaHb?zB(l2_%_E^SA`tZA;QMw37Nt*As~s}A zRw#makya*ms?`a4tfdO;oZMg7r0bQc6d7$cq@L&pq=iFqzE zM$TS{k$UjMDvyS(@hIIye3Z)&{KTb?Cf>I=t-(yE*+KSo+N0awusdwz*D;oucE81A zp^6iX32mPkjXdxJ`rY#S{DPU;N=Lvv~?3C7tEDs zFs+Ayr68b6Rkyi740TPHtZ$p))g@EUKqlILgzQk6kg@iB$RfGR=CrrJ8Etctr4EZT zm~M4gh)<%UT*2*z=qMu7t@5zN8kY*c;E9Z3f0DZ+cf>%2&@qpR-NHfY7;B8lON+%c zIH4e$%F3p4s0ouaS)$A=QMSnHm-_tDOlf=ulb*r&7=NldB~_iKE=blVL0#y|;*If; z9b>h};(^RqYMpim)SzvQ#c75*b&QX4>7kaR?^St}Q17n$B4eb`67HFF3=NIro)r@l zGte!JiG^^DJ2EC#>WLiYQh8#OZdDx=!mW>v8VSXUHaje%pr8(i%{dC>SV7)MkZS_@ zbx=J~NxI}=kS|S&D)ny2ry^aRQ3d&q^hNl~p&a5&VOII1aZolv7MU$556*e!xrk7q zq2Mq#>_JgNKlNEhkF`P%Ergk(4mF~gXc2lDZ9{LP9cVZD0UbhTpnoHmKTYV@I5Yei zX&Tbw@mmQwse^9j@%vSN(n<=2RsupjNtegtvXXwCUn5MW{Cr2$>HOe8Wj)|v1cdPp z#yA+!?A>4(M*#!lD8!PX(G+N4;B$41uaJaSai_!KR4a8tx7&m5*psHSXLwVw5AZNP z^4-8s@_4(dBNU1ebc+XmU2+87V0$;1e1DO0kv@N6uTws4>DbW%9Gg{Z=|c}2A~cLj zpFH-}`q;=zB<&Us$c~TcQOzFkY4O(%CV8rZh)POqE_WB1@^1Frvb#!CP$)0#uP~|1hT>L$+#C+bWe{`n>Q$jib&_H5 z@6JJE2LAh@QGLg~WHOnsJI;aWKKOKH&+@$9yS-MuotmSM9d`F9d$ph-QIeq1;vB6$ zH7a^Wk*a)-C(3Q4OalW-X@4pO zRGH=|gENXzD`XAqj(O6-Tc)xL@E@t(QNbNEHg23TYvacI@xR#Kcu5Xh73Imu@HqYc z*p8SN^>Rk1W8`{Kl#B;tQJqhi*^5ysX{Rs1Of(#3mv>>#K$xaIZg+~;?V;Tsn1Df{ zVA2iyfCLzjT3Q$abXwX4^faB~1vPVfyr88a0=L^Ari3V*J@pAeq0XE+~D(o z8*Bvr2XbN755fGzAD-_?9ViqpPYrY+N$B>abg}DX4Kh;PkVcEUvg_4$(z~GyemCv$ z`%=Mv5PG#b_=V{30|{k%n=O&n7#O)hua+q^GE+pPI8v)62}&M83n>9XkQSx1B#yv% zHDl1wi8h4oh0!l9ZaEgh)9b}HePp&IhLmaaj9K6|(FSP`je;Oa3KNXTD3{1} z8k0t?cgA_5B4rA_-XM!JYa$tu(lMSS35BMI)IggYN)Z#OF~`Xadc8sx8Rdy{>eY}= z2kDI>hQRm7WJc#Mt*BU(m$x-D^SgrV!t(OM?1JwyGq>jDEvl$knj4)NV=J|olatMs z(%`f4W&`2q6&0n3Qb`$t?iCxa)w*3CMWoT>v@`MulgT9)=}IG`F?-Wsl&jp)E{#4h zwopJakt(PLeXfhpnBz50S*cbecUjC5hOr}tW6=?Mnox_1z*-uBhM{p_Bi#oUQZv{` z4}x{H7Hp$UU=tzib?_PriV`*~LnEli9i8H_hm;Idf;TY3+7ec^P6g#PQX5iGJM;yi z)+Y#2fzOYnT7E1AMeTy2J2)L$A+6N8l`gGP=u?sq^g${fACC2h06+r96M|LY40R6p zFuX%w1BIujtuLqK60%sYDKz%ITbF*1PCF^1_N`#Ad)gnjZ|u}X-h3qDWiRy5P>~Q z5a5ZfQUdS^nBYV--b_lPq?RPQSENuzR@KxFA!X|a{Wx#cmrCiBVn?}Mkzytcpm&js z#z2@;6!vmQ@pzT;%T@D!97Me(EK4Esa#PBLC3*d|R*Oz&v1xP`t5#z(*SlA(I{i4i zdEG8tF?lk(W%p`a%Q}X|+ZvX}4{JzGoUwH2j6~DYHxrkyTem#Xw2pj5Bo>QAk_chP z6UI0RcTso_-ghIw`gX%umxD^t-DnJ&jaHyF=vDM4`UL%ge#aO`;dtB!*Wd|w0bY*R zs%hQ9@J^qligFGqIM~MC>H?5*G-LRFF00iMo`n1AsqC-WXq155>{zb03 z${rdZ!em0Iv~EfGN(&7`{FDi~;*GzKcb$$wsw*4nP(z1W3d-9h7pf6PF_6xWV&Rb} zmB&EQpqQ>s$}kxq`QmhCtg}{u3~C_Evx_pU)!|y|n$aN^eo&uXIrn$!JYSii;gu(F z@VV{Y?p@q;T3*MxD503)n&~tnI+$mK%Eg-jogE7J1Err1*%`dwNg3wm<1+?7C}6lC zyp)iRY1B&4TfTleWw}B$;hG1P3(<${A->B%RfK+>#K5z&hIvZ}bbzN%O$}9Ah%(fm zkUjW?8iFs8E|fz)VP_MBa<27+@DLl$WLIv1Gcbfo2;(6SU2rJLEqqEy90>As%I&Hm zXmP4ALL!n%VbsKHMG{eDBzUYeS|^o8SR5K{GO@(&B6KEy+fYlAIz!?yM!N*tM%kiO zBF0r!W{PjWTijQt8_39F$I}7@RViWQsz?PTVTkDB;s%jSE{~6&ObaB8%GUP%ZyQo% zW+uX$RW`4eOr_Rj5v!s~Tsn%91*r`7{VGSk*4#57ElzRS7uxN(H(ACct3{NRkicXV zt=0Io%HkZOA=n=45fZ60XKWoQiyygVd?{bw>fFfVv(-T zgEMrs26GncAs8_|IWv7ltl6%!+H&oV#LTQJ@=3W`{a|^lUMG!8_QVbi6!a7XW-RR2^8P)U z4auf46;lqi?HXB4G4{k9W3Pv^?#;0ciPu;n)UpBD+WFq}H0rg%_XcVgS9@yA>gnYb z8}LNES>z!IDG@=6Oaw9YaLNLj?457a-~1@>T%I~HQpV&bCO<;HM@=kGjF%|l%RUuJ zDC(XGl?&`rMU=6}u@?ERN(|a7fO)8g;5fs83ByewlvO9idh+lvS-W zv{fn}4)hqVl1SxJW05arEcKy?ktrmeKu$(oZd8s&TjufPI9>X-cRa_dDktd0l944D zYto`(gWr=9y*$fWU8|BAlgrA=rxA~c9GH0W+scP!Dp&u&GuxFSD^Xb5%WQ@vJCP{S zXG=RQCaF===d8gW56nxe)VR`3)>4@+CRi13l4~hSuJYVtj(s>$W^Y$XRE)hg)(|~n zaJns1&@eGQws(XGEXSuBOM5S=a(#EBvB8{cMf*J zno0a;)HX`5vy0V!Xmp86Zfd(vws`Q}ZnA_|6|CVU!ZFQf>6~blQ6=6?Umaq7|8^B`TfC z_WWvRPn~tVO|JLTOi6y}veM+*!8OmDS*r(2;^Rh2q>{nQ8fHDJhF&a+kVxN4Db2`O zH^Y^ioY?Kh}oo9?UFj`uc zoHFg7TejUGOez^IF=U7hB8fz#k4htRk2B`;(*L=BQNZ#_|1k?7GCmET_`k5Z^Sxe8PN1#2GLlj6y$I~%26eNWZGp3#Du)U zU6TiI6xr+U8Mn-nRyJqD+vZ{^l^*NKigi`(Ge!8b*!v~s#V^(@l(3_V7i(k;k?2UB zYE3S8YNB1Lc%?Kgma&RtDsAk9`)XZ9a<#EEzj^)Qw2Eg$${JtJRFT-8Q$0nS+%q{P z7TzvEh~~c!klNmfhEtTd80eiFIL}GFva1H@I$U z^+Kl9KZ|{0Xc>(8QO5lXd?l~FAuI98i6zGPtrZNd){RN`%^qMV$V*|wMw#Rj zcRZ!l6dAKNRMp;>!uVdJdh~Er8s&rY%W7iTXNC@ojSwj*Np)k%^NI0U@scXL2V!W;_FIKM9X+>1b;d!U*SE2Yg{fElI$gNhdFDMoR*042PmAeDw?>9(duQd#?P}_ zJ$k*@ZZt#{Y8kb`P?0fs)yjU>cvyL1VoQ6xDT{E%dghotDH?6{a%*on69t2v+G^~( z9QGe#JsM?l&w6vROkq(n@?LrsEZs<@5>ae~#x9kKq#{u~qjA98tP?3>C6OYU6p9q& zn_w*PotfXkX$0@Q;13jq%qRhQLvI-=PA}LN?vQCkr#Sfq7T(HJ*%fx&X%h2BmoNmb_pv>snU17hy^>tn5tj%e2wR>4*abm?ETJ#7qN+kMC>?U;QKXUl?m#+n zI*>+<(ozvt*cD)lrD>dCNdX+Pq`Ge+5!>qPy@wCu{p{(Fcd{q2VduwK+x{c*O4T%V z(*o7(yMlL(Te1|tqp?~w;K;QYSFv;IGj?JFdt&Fu>}jke*5KW2QqOG-zfO8Lm+gT+ zEW7dWLpRvhA%VpLiD2K0LVDqB;fp8^R(^9~U#1k+8Td6Zu&s3Vu+iv7U}3_$1Mi0n zX7It@gVzf1u}_(iPCMO7K8-sa%tgTHU=@j98*urQkO?7R=$)Q1j9fvqPmi{^M*4j0 zyQ}VhvaCokymF&n(z|F-ok%-y*sxS0qbaAqqy52+>chirG*Lb+$;@b;@wBJ3T${)) zncqm=J%*B+cqTdAHRSjqP5Jg^8Zkdpu>6GB)I zPwPp@36GqBwQvvI$%G#$U`NVId2|q@{IES0S_bD4d=kG07RCKml8^RyoIpairMOe$ z39U)pR#fuxigam$`^7a4g9kshZqSrb(c2KCviQrXQ$lg?l< zMW?zGEZP_)6CJ%~^{hpKfHvDAlTs!+&+8UBEcG&VgGuU3ka(pISyHjoQl>QNZH#cj z`iS)Br)4_4qKP9XJ`iBi<75RZCy$Jy+;o7-tf<}YV^V9BR1X@vBw8hbW2um+3u%Kkpr_{7 z&YV58g)-$O#fhQ~dPlm!P&U7MGeY?&j^E!W=YlRbqb0C!xC%Xno`jXL7h!E|3;Gw@ zjy^!UV88Giv={A%-NIw&cXSS2hTV)HCa@4ou>xzc2|I8M_To&OhYN8juE5oJD6Yfx zxDijmGx2=92rt7c@uPSRtdzWnU&Y(-zwu7|DgF|Fi+_Ywlf!&Z_Xu?YzEPotbwW)@ zS)fsc(C8sefrge7pbSpl@%xm?gia?XGWeDUBB39aveQ7NdJ>^1pamS!6gU&n7#>M&?2q~^g; z>OUpag(Csqks?H4=UIoKaMD|xc}`(sk1&JJ4d}TaG5`;k2XY3vbMd(WIsx2 zp*KJ&dC0@}41fWEA`1N(|CtVI0qFQ2&pzYHTvq30;{463ZjNJ*laeb$*Ybos~Nqhpe8T7T%Oyj$Zs$t#pe$kq>IW) zEJ-UVNQ)~U+_P$!4}iP8<$W9Tt%Hr?x?cY}ivyV1Ki!*ioc*oWsD%9X`wUI@%wT8h z?HY|>QrzRy;}T3Q8da^}dYd&$vf+!N=r=hzFWEq>jyYSGGqe?&{s2++eC78MjDXJw>|2P+OgfkGS132d-R|P+7$u+$^ap zVYe@dE-oV;QRfC6!5^4ZuL*CJdGn;i^Zk+xfp%Py+cTGRRq2yrg0pkg(GFZ+MJKp% zrqrU91s_opN|_SZ5_(Ea5sfAf?b5iN?TTc(DLGD*fGtZZmrgb@hT4|gdrFH<6N{tk zDlC~b+JK4i*_1N3jIk)sl+_Wbb;YZfmdWy_HRY8z)Oc59rq&ORiRoMXNu zRMdmHwd%$I!4Rp{(!6Zi7bEd*sn~~rldlu zuSifD+?3IwRYZxUF&P$RanKZ5Ua=vU(XEttIwnk1e(&m1^BXUszQ)zeTp*SF%*SBz^FbDw>edtEtHi zW$R^)f%GZ*$;Ix`4SAI{Wl6<_L~T++Wqn+Et|_-iu~W*n(!~Ydp>%pubg?wgWHUu; zi<4p)k;7o3C3@PSHVsH-G&-#*Ws6YZjgB-F538t~j+^V247&3^^4Zt{^Cf0Tl)^pn`!7#w`8^+u!^+$NzLa2`kZ)NG5?94`LX3 z{5lvPh5SekV{(S0>WpD|A3lN;l5GjhJz8FTPwebYbsH%^x2eejn0z#cI*YItqq z;K>70j1dlZT6%&&dDlg|Q@BD-@U)&R@4?%c6^;Orp4? z1!Kp|nlnFILiV+st+S`u>!3N?zZSB_=I)GA{1%?TPZl=AA`$I?*VlH$A@Y z{xrEj>72VXVbJKgedWEKQy++a`c>z$Ct{-gCK2dg?YOyf>+-B(lgt!vms|SHVCU#e zDoex=qcl#bG}5pqCRfLU&Gi_1Lbyzr4rj?!&=y$5|LiK2z%C|8Oe(N&S@6Jj!F|Df z-v{>-FMszPJ2ChuH3na8d-?<7t)2Kf^%{F`a3+43okT3f4-rd(v!mG6xRG5Qgq;ea zU)vb=5wLw3q!6AK{t?Ye0fd#Ri=$9J&mYv2<3w}!^;}v)X zk<8BiX7>zTQM@--9sK9+PsT>|Npkg);{!IMu}@B3mC0nCS03#w{WAT-@`-O-%guKB^TFY`k!Zw??J-1SaP^-ZZy42J(pU>~}xkxZ3gkZ8j zPge=~(=ED?S>=MF3BZ6M?HaoZy0;6=3L!KNCfFy34H|A3KdDl8SNRBxKltc8dmu&o zy2h&sUARZbQ+oLAH!lzCTV0O#7OT}}wb{Uj zht*1r+;DdPTVu9WkGQdD(e;scZ5!XdnIQY;KZutv$B&k_ZzIU6=}*7B@nyV;m%$f; zm(eLS5@0wY2`&2?)HJ5xyeC-Op>vi1V`%dyJMaY1Z0LXmpaokyU>fvccLywnwmQ-Q z%TPIL4a0DZkAV}(A^vctkHIw^FadBw2TTGyvje68UfKZ*0A2&I1oERonfPVUg-K{K zng!$gI5=t8fTkfEdIhDREV$EbDwGOq2Pw!0*pWbGgA-5E8Vj+Fz=6l`@qAg5;f!W6Dnp4O`6*B@lYnk4(DAi%XE^na z__RC1=`g}hg1X{QF@(!>dpNc8MDowMznQ^Z{kg|M5jYJv2e{{c?(vHf+z@c9e(v$> zwx9EUSq5%pNc+G;h?~Qo4gZS^1CD{2g$~7=s#0vWE-F3_6t^n%PdHqa_9q-smHsCj z*$?=I&QXV1@%*TM!0!$moE3*Ceq2Pb+5&SMT-!oE!cPzv1pCBs;zi;g#V4f2(q|*h zk$dFDjFHJ@HZfl?!9bX>!@oVmBfDdE@(8#BA^(jA%t%>(~& zb}IO%+4q|Axdtw^N$|@HXlOJn0qjBRn(}cLmVv(lH-P^E-UR+Zychhv_#pVN;y1w` zhgX0v!&y!FFi?HggdIbGWi0Cee_%++6YMmAFSENr$Y?I5NpNrj8tece-*gCx1O|^{ zAi^Kw&EW6D--G`HJ^=nk{66^O@Ppt>JA{1QBybKvp`s>pqk;Es_7eD0*~h`pV{5^W zX5;@4tCc!xSbSU`;FS2ZF2KHyFRB7;J-#dsuo<|W2(WI9+X8Gl7GDmqW3c>8fZdAK zCxBjJKDpR*3>i<%i8>iT=dk8<0GDI^g#dmENBj}MUtj?n!25AK$-`I0JpQ6SfcxXq z8lb068s{8023QX!fxiz+uJUq~=L75;SaT}CsU-U2ODrd_`S={ei2yo}HNOY&0L+{Z z;B`3idH_~Zgz(~f00#v;d@X{ht{IzDyOYiKxbQ4ykrFaoC)d_Z@ zTXz8ZA%BPZqsa#L$svHl*XLVMH_DwIlmR$y8$d{VsDlcjy%o3UKHqW&UDPeQ)7|(T z0?vVs8{NV0YBzqTyYUOrnQR0y-Qt$o?4BJWGA-ISajL6#s=i^?uCwcYNZ4$INz|A2~ z=o56E>GE+LjwjpUTaqT~+Uc7SQC$(}Fgi+7pYOUM7*u}t_!W6)1ezB>O{DP1!5f0c zao3NWj_8aKxMGQG7j6p5N^hJ!N5IPVF|-R>d>3fuGq*JNn{F+Afv^2%>*&rFWV*$9 z`M8ybQ0Mgx0G)LFvr}6R@pQ`$b&{bA-RKT=qeDF1id%F{H#(kkCqElc*K&)_+l|iF zjgF^}kK;!Lg2uRGEa;lQh%TrLC_;~OE4h2XCR)tRX+eF8&_+1_egWPPpFmB-u%Qiw zXc-)%dk0=q-)cf{6>@iR2^<3x(k!y+9~;&bax=JrToE^(>(f$%u5uAv1iFe^Il2YS z?gyz3L3J;N*UXzJ$bT>c3(a}B2(3e#o6wSeTnT66Oze;B4Nl0BoQM;!Ti6fTcVH%} zYWcq?1y;iuz`repotwnX{clTwvvJ~oD21Ed&tCnDQY0sJwnbNs|3^ST&R)EDb^tAm zSlRQ!6(oIZ%PDQ#eF4;rXH5^F*O-YThAD*T<<)MAd^8IlJ$_C$Z$W3C*U=M4G+K~G(jqI} zwJoz?=V$H+?xw8GkP<*k#FM)6(<~nl)2~wU1LXMAg9m@nAaY?+;{PstqWV{!969tK zVnj4M*1Y`nub=rwk8c>8re}_RhJ^d}{dRH7mVdwU!ke$WvUU5rq6n0DOlP|IyXgvw z!i>(NU1^O*XB$fD#k_LvH@uv*L=ky<^WArL96z*AZNQgo8e3NXvOc8)JsGL>3CElNx79*Fd3XKOn})P4lvFp%JJF?2VYAK;z8fCh0Os@O=6Ap1mb|W&;o8DCi%?hPW%U!-Q&WdYiPyXoH7%uy&k1sYWKG}cp*FI$(m9zUZQ4opC-`kpB zyuHm4*DHWliz~uq_tz&hD-ujr;pqb%l_NHMZ3-Qlk|06PNi+plo=PHY#UocR6N1tPV4 z=!o(6P98UE+WY`|oS5A(rvR;l#fFE8d1Dsjqo+|bF}`*(On*Hm4)A9gg;I^+t1ph9 zIdbU0!Bd|6GZL$~{o1KZk#YVst18NwkuKG$HR?!2T)%Rq_3AJCQEMw3Y(LAYs@lvS zu3hu=b>jRBYaZJzNRr{R%19}Ne!D2K#uiphTu_6SqSYXJIa&;-t6zX`N;bjd@Eklp zgRd1Ihi|AJgEzdLFoUc>bKyJ2&FCrb%<)wvZnb(}BzNJ0Kp!1R+|;D%MN&&fbQCR) zI4gJkdh^h0pMUbnzOVKkxOVdR5so~jzjsDI^Z>jyFNKr;kHdFaTj7hy)u;uy^4~}< z(axATXV&z_(L)B@U0oUQWo5_osUAKsRaC?&IW_0xQn?&%gyf-DKKx+wRq4ssHa;ZOH1XfEj{;~#^*#a zx~tbG%$y`gNi{PYg%Ri44)BTqcD@~M@ph)4hZN`RY7ta|BE6*giK?86>_Hw^-IU`ccd)*UaPHuM$x8tUw0 zm@KzIeZGKJiRE4zD9XO1mRNFoAQz3_D_Yz3wKLSMrJgxc%>cx@x)^jE44I&rRw1!0H#CU4z;#U$$_+{XC|jPK?OPd@?#aE7eckz#-zwF<8BC_o$(n&?Fm~rVe3So3=c2Ztmxo#jR%OGwt zw-8(vmj~)n&&}h8!gBmDlPCU8Tz@|dWqx?1=?>;>q# z|3v>r-@)?2m!OTGfU$E7H19mtqSK(Y7h!}qVj-L*_yxW^Mxeow=s3i`hF*xP&2+u^ z(!(2{-~7?*FK>N!|CU|f9XZr?=J;=CKY9I4p;3PB*;TLq`_WY^pMG}Dqg4Y*Ojx?K zLrio|97e$LZ?C-8b-l!P&~;NMh@z#Stq`LR$5QZU7BCE>Vwe_!KxY<(S|Shnnv4=m$2V`r;XNVT^43gexX#)EQ z#FErx6ca#iwvaJ4=hw{vv{AvNhSSMH4+&jDH%rJ!8I2;74Cvg|h0U^4d%wMi_Fgw5 zapc#js>=$-8S2dfUNFWCpMt!4;pX*bioJUA=9z`foWB)#q@P+ayg*p}55J@C~8U%1fGa@jepzd}dq$f>CbqIA{l8yWuRruziK*z1A z3ZU+EnyOoLl8v|MtlaH%aj)E>vvuQ_aRq!BhYh~Gwuw+TehBevI=AjXS4$vRw>|`3=e4))7S-ol{+{koH*S0D z{+^Cs`{}$7bx95I;%<|BwVT}2|3Pj>MJKnNz~YwNz#aVJ*8epftTo*x*VdK0+xf+< zznxC=H*#%v$d&w!TulW9yG?L$m-As-d7b=0b(b@YYC_lYu>r@CpT^e-JE$&_`S9(W zp8O%<>=PZR%c6WRP1cb|%-^mLqsTS&W1hq!Nq%rda6|{yH71{{8JK|pxWB= zxl!CmQg`^2wj-gmZPCM|_FPnMN0_?4?GsXPB|gv*7PcNaN0M#MxQ_6eF}xtMyC6*G z!`f;&`K34MGYYAH80p5Xka6z)Ze(l=7$C3IX#W6Bs-Tha?G^E~e`cxCKb`9zX zE3f}>j=Zt)ZMq{YI`Z`;vh|CDqK+`}$I&*j^~?>ZEM6{oOi>&~M$E3i-Vr`=O0Ofi z`Ucz)zOeU(lDyfwlI#dy`Q{i5yU`hvj_}PRzhm--L<_5{0@yFKz`WsvcD2GdXR!$p ztX2x?LJ$NOad5}UW_**)XY1K}&?C+H+$46GknRzV&4+JgUq$|AG%kRS76dN_Z*Vh# zga}>{((yn7GeVPK>xJtAV@7`PK=3=_9_875u5a)wA>B93fqtg>GcMxHclqpb_D6D? zpnY|K-6NzWF#Ccr*F@+H7hoR(9cgM35XuRPH~5Vu5xQbS8R;SI;<>>7_lt?znwy~eVD$!o{BzuK3aZ;`)*l1(H9-GZA z5G{M?(X}tHd3b3{FEG+3aAUY(f<@2#cry!?z`Mzt=vz1qxf?9*kHBtuvPsb1ns9W- z!T>i&u>Go*C!2N>F=zaJYY;N*3 z@~BnVOqk4l`ha107%E^7yn=j#W#~sR@Sej{Eq%b+*bZ{;#|(Z1KMaRKb?7TR!rqK- zG#9a_!03};4bWc5Fy)DhDsdi8vMChrQT=Yr%HhLez~ZXvX<7`@mn; ztU?ci{}g%zjL3(8CN&=;^uRUpgCXQPbf?A+ct#bpf?obXsm={?twMF4^fL;`z2gEE|jI` zPOQ%?$j%I)m8zNL*`>wQ=`*&#{l0rm|09Vg++xFim--@(RTtKbKHjOFyMKHNw+;<$mW zWD8+ya3q_=2H0z&*M2^8=t65dd#Q+<4V!E)vg_F&xXLDBoY$L_9vAn_0(hC7&WeLq zf~`U5H2>4hN`oxR1b49oz@@#y~9IMp9Q#b0QZJnNX~oB0d5$`ss-GuAon6XH%s8)Oy||7eF--Y55$A0ayU;l zkelDWgdN(8Sb`s-bm)3hFHYR{JDhHt-E>ru^V+_Fjot(kTf({cj=@p`(0`rh}Ua@aOHyrrtz9&eC3p8_6ATVD1f= z%etpQ50qdZx3v9x>?6;yP4LXbzCw02pMX!;T)-zGckts9ih>q0+u##Je!+w$3T`}b zdc20viJ`d)67~!N5 zw<-$eYG_la<;_rqRW0C}yTPIMzLUIdKue%)ep7&2*b<=TG$At3HX1??G^4fvHMc3y zHmT{3B>C)G_5s+Se}Mf*Q$FsC=Yl^Q^Jfua*_QyW!XJUZ8;=Fd$84-f2=7{<8{|vD z@tSc3+`v$N!f+)Y7pk9lD4p1bBc)<^Eezd!+mzv{Fn3HvCvhrFT_>T(ZGsa@OW{sk zL0}4sO%%dA9n`NH(ihq=4d+URU@;D`EGQrW)xfqe@)}^p&$69!Z-DCqwoV`PW;1UF Xy~X=nDQIJ#06ZY#j_3gYzasw+?$LNn literal 0 HcmV?d00001 diff --git a/test/api/test-ot-face.c b/test/api/test-ot-face.c index f2d2fca4c..9ebcb4e2b 100644 --- a/test/api/test-ot-face.c +++ b/test/api/test-ot-face.c @@ -110,12 +110,21 @@ test_ot_face_empty (void) test_face (hb_face_get_empty (), 0); } +static void +test_ot_var_axis_on_zero_named_instance () +{ + hb_face_t *face = hb_test_open_font_file ("fonts/Zycon.ttf"); + g_assert (hb_ot_var_get_axis_count (face)); + hb_face_destroy (face); +} + int main (int argc, char **argv) { hb_test_init (&argc, &argv); hb_test_add (test_ot_face_empty); + hb_test_add (test_ot_var_axis_on_zero_named_instance); return hb_test_run(); } From c8420109ccb74a7bf15c4af425f9f58bc315d2ce Mon Sep 17 00:00:00 2001 From: Michiharu Ariza Date: Wed, 20 Feb 2019 15:48:29 -0800 Subject: [PATCH 036/101] 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 c9b07c75a14b6efb4b32cb12b2f3e8cfc1953638 Mon Sep 17 00:00:00 2001 From: Michiharu Ariza Date: Thu, 21 Feb 2019 15:47:27 -0800 Subject: [PATCH 037/101] 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 c485b77c7c1df2fcd2f21107692d1afd0ed34e1f Mon Sep 17 00:00:00 2001 From: Michiharu Ariza Date: Thu, 21 Feb 2019 16:34:49 -0800 Subject: [PATCH 038/101] 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 1b13cc775c3b8143b1218e205b21b91b0852f8bd Mon Sep 17 00:00:00 2001 From: Michiharu Ariza Date: Thu, 21 Feb 2019 16:42:30 -0800 Subject: [PATCH 039/101] 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 08dc86594bb17e31df2d5c0e25521ee1f072871b Mon Sep 17 00:00:00 2001 From: Michiharu Ariza Date: Fri, 22 Feb 2019 10:22:08 -0800 Subject: [PATCH 040/101] 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 8563169291bf257400608aa0900fc3ee8c2f9e8b Mon Sep 17 00:00:00 2001 From: Michiharu Ariza Date: Fri, 22 Feb 2019 14:53:06 -0800 Subject: [PATCH 041/101] 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 f289ffe5eaf1c8cc87f9ee80f77a4af974919611 Mon Sep 17 00:00:00 2001 From: Michiharu Ariza Date: Mon, 25 Feb 2019 09:59:27 -0800 Subject: [PATCH 042/101] 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 eddbc9d0dbb09589a09e7d8e661004cdd7487e87 Mon Sep 17 00:00:00 2001 From: Michiharu Ariza Date: Tue, 26 Feb 2019 11:11:50 -0800 Subject: [PATCH 043/101] 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 0b1ae2385b5564efe3dbcd5d068462bcd55effe2 Mon Sep 17 00:00:00 2001 From: Michiharu Ariza Date: Tue, 26 Feb 2019 16:54:00 -0800 Subject: [PATCH 044/101] 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 baf2ccf1471856f948ed8d2d46e4d68b0c0b739a Mon Sep 17 00:00:00 2001 From: Michiharu Ariza Date: Fri, 1 Mar 2019 15:14:22 -0800 Subject: [PATCH 045/101] 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 ae0a557c2e56064d204ae549a3be135dca884f67 Mon Sep 17 00:00:00 2001 From: Michiharu Ariza Date: Fri, 1 Mar 2019 18:12:31 -0800 Subject: [PATCH 046/101] 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 cf2ef92c741a20158d15d7ea7c14f6ba82906d36 Mon Sep 17 00:00:00 2001 From: Michiharu Ariza Date: Fri, 1 Mar 2019 18:24:56 -0800 Subject: [PATCH 047/101] 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 2d7ad3f28ba3a9c97287eb9e1bf75ad353a908c6 Mon Sep 17 00:00:00 2001 From: Michiharu Ariza Date: Fri, 1 Mar 2019 21:33:21 -0800 Subject: [PATCH 048/101] 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 5ec65f779bef1ebf2479f83f4b76f4e24d69ddad Mon Sep 17 00:00:00 2001 From: Michiharu Ariza Date: Fri, 1 Mar 2019 21:49:04 -0800 Subject: [PATCH 049/101] 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 dde8bb1f6d0bfaac283fd8c2ad15e354c233bac1 Mon Sep 17 00:00:00 2001 From: Michiharu Ariza Date: Fri, 1 Mar 2019 21:54:49 -0800 Subject: [PATCH 050/101] 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 d23c201f5d8af8f9b38c666e1ba6525d38ef0806 Mon Sep 17 00:00:00 2001 From: Michiharu Ariza Date: Tue, 12 Mar 2019 11:03:53 -0700 Subject: [PATCH 051/101] 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 */ From bcb4e505d6ffe33e3268a06698e75d6be0e64957 Mon Sep 17 00:00:00 2001 From: Michiharu Ariza Date: Fri, 15 Mar 2019 13:46:25 -0700 Subject: [PATCH 052/101] cff2 subset fuzzer issues (#1619) * add check to FDArray::serialize * add test files * fix off by one --- src/hb-ot-cff-common.hh | 1 + ...se-minimized-hb-subset-fuzzer-5739000398086144 | Bin 0 -> 620 bytes ...se-minimized-hb-subset-fuzzer-5760768497156096 | Bin 0 -> 210 bytes ...se-minimized-hb-subset-fuzzer-5764268627066880 | Bin 0 -> 687 bytes 4 files changed, 1 insertion(+) create mode 100644 test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-5739000398086144 create mode 100644 test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-5760768497156096 create mode 100644 test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-5764268627066880 diff --git a/src/hb-ot-cff-common.hh b/src/hb-ot-cff-common.hh index c645953e5..61e615cf0 100644 --- a/src/hb-ot-cff-common.hh +++ b/src/hb-ot-cff-common.hh @@ -525,6 +525,7 @@ struct FDArray : CFFIndexOf for (unsigned i = 0; i < fontDicts.length; i++) if (fdmap.includes (i)) { + if (unlikely (fid >= fdCount)) return_trace (false); CFFIndexOf::set_offset_at (fid++, offset); offset += FontDict::calculate_serialized_size (fontDicts[i], opszr); } diff --git a/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-5739000398086144 b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-5739000398086144 new file mode 100644 index 0000000000000000000000000000000000000000..0dec23fad3d427bc5ca05307b3e6f08155e7f209 GIT binary patch literal 620 zcma)2O-lk%6g|)J3myC11btxTA|gtqT?L6T6qH5|5oq9~#srOung~Hki#7#qT17!i zA?P=>Y}d!4`T;=^L8~BwnC=^+BBHvFciuba-23k3g=IO6I#htT5R@eU%2o6fzyo0Q zkIx2XMzqHTz%~a6!&Af$pQz5t0`ZReCvCX05jd@8FRE@ zg4$YqO@`wz3k+j?gpX81{~2<~V+k3g;f5dm@Dh441eLS`5{6SGkN)VN@#j-M0bu-B UP%f7%w0YE~W1(N0*zwJN0~4vTOaK4? literal 0 HcmV?d00001 diff --git a/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-5760768497156096 b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-5760768497156096 new file mode 100644 index 0000000000000000000000000000000000000000..063aab2ebc7f2667782a22e8a9f32329a1bf7020 GIT binary patch literal 210 zcmeYd3GruOQ~-ki3eIkBMj#dg1A`feMg{+oxJ(MH48pQNb@e_o8!nZu;O${;KY^j$rV z3W(|#gqR9sDln2@fx>@eQ^6WB1h5zfv_=cr8X%tmi!zuH0~Tvwa Date: Sat, 16 Mar 2019 00:08:03 -0700 Subject: [PATCH 053/101] fix tt var extents & add test --- src/hb-ot-glyf-table.hh | 32 +-- src/hb-ot-var-gvar-table.hh | 270 ++++++++++-------- test/api/Makefile.am | 1 + .../fonts/SourceSansVariable-Roman.abc.ttf | Bin 0 -> 3240 bytes test/api/test-ot-metrics-tt-var.c | 72 +++++ 5 files changed, 236 insertions(+), 139 deletions(-) create mode 100644 test/api/fonts/SourceSansVariable-Roman.abc.ttf create mode 100644 test/api/test-ot-metrics-tt-var.c diff --git a/src/hb-ot-glyf-table.hh b/src/hb-ot-glyf-table.hh index 538577a1d..a494bbebb 100644 --- a/src/hb-ot-glyf-table.hh +++ b/src/hb-ot-glyf-table.hh @@ -299,8 +299,8 @@ struct glyf 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) {} + 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 @@ -331,14 +331,14 @@ struct glyf template static bool read_points (const HBUINT8 *&p /* IN/OUT */, - hb_vector_t &_points /* IN/OUT */, + hb_vector_t &points_ /* IN/OUT */, const range_checker_t &checker) { T coord_setter; float v = 0; - for (unsigned int i = 0; i < _points.length - PHANTOM_COUNT; i++) + for (unsigned int i = 0; i < points_.length - PHANTOM_COUNT; i++) { - uint8_t flag = _points[i].flag; + uint8_t flag = points_[i].flag; if (coord_setter.is_short (flag)) { if (unlikely (!checker.in_range (p))) return false; @@ -352,11 +352,11 @@ struct glyf if (unlikely (!checker.in_range ((const HBUINT16 *)p))) return false; if (!coord_setter.is_same (flag)) { - v = *(const HBINT16 *)p; + v += *(const HBINT16 *)p; p += HBINT16::static_size; } } - coord_setter.set (_points[i], v); + coord_setter.set (points_[i], v); } return true; } @@ -366,7 +366,7 @@ struct glyf * in both cases points trailed with four phantom points */ bool get_contour_points (hb_codepoint_t glyph, - hb_vector_t &_points /* OUT */, + hb_vector_t &points_ /* OUT */, hb_vector_t &_end_points /* OUT */, const bool phantom_only=false) const { @@ -382,8 +382,8 @@ struct glyf { /* 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 (); + points_.resize (num_points + PHANTOM_COUNT); + for (unsigned int i = 0; i < points_.length; i++) points_[i].init (); return true; } @@ -408,8 +408,8 @@ struct glyf } while (composite.move_to_next()); } - _points.resize (num_points + PHANTOM_COUNT); - for (unsigned int i = 0; i < _points.length; i++) _points[i].init (); + 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 */ @@ -426,19 +426,19 @@ struct glyf { if (unlikely (!checker.in_range (p))) return false; uint8_t flag = *p++; - _points[i].flag = flag; + 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; + points_[i].flag = flag; } } /* 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 5c157a1d5..5b5a7884b 100644 --- a/src/hb-ot-var-gvar-table.hh +++ b/src/hb-ot-var-gvar-table.hh @@ -159,21 +159,36 @@ struct TupleVarCount : HBUINT16 struct GlyphVarData { + typedef glyf::accelerator_t::range_checker_t range_checker_t; + const TupleVarHeader &get_tuple_var_header (void) const { return StructAfter(data); } struct tuple_iterator_t { - void init (const GlyphVarData *_var_data, unsigned int _length, unsigned int _axis_count) + void init (const GlyphVarData *var_data_, unsigned int length_, unsigned int axis_count_) { - var_data = _var_data; - length = _length; + var_data = var_data_; + length = length_; index = 0; - axis_count = _axis_count; + axis_count = axis_count_; current_tuple = &var_data->get_tuple_var_header (); data_offset = 0; } + bool get_shared_indices (hb_vector_t &shared_indices /* OUT */) + { + if (var_data->has_shared_point_numbers ()) + { + range_checker_t checker (var_data, 0, length); + const HBUINT8 *base = &(var_data+var_data->data); + const HBUINT8 *p = base; + if (!unpack_points (p, shared_indices, checker)) return false; + data_offset = p - base; + } + return true; + } + bool is_valid () const { return (index < var_data->tupleVarCount.get_count ()) && @@ -211,14 +226,114 @@ struct GlyphVarData static bool get_tuple_iterator (const GlyphVarData *var_data, unsigned int length, unsigned int axis_count, + hb_vector_t &shared_indices /* OUT */, tuple_iterator_t *iterator /* OUT */) { iterator->init (var_data, length, axis_count); + if (!iterator->get_shared_indices (shared_indices)) + return false; return iterator->is_valid (); } bool has_shared_point_numbers () const { return tupleVarCount.has_shared_point_numbers (); } + 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; + } + protected: TupleVarCount tupleVarCount; OffsetTo data; @@ -347,7 +462,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::range_checker_t range_checker_t; public: struct accelerator_t @@ -404,10 +518,12 @@ struct gvar if (unlikely (coord_count != gvar_table->axisCount)) return false; const GlyphVarData *var_data = gvar_table->get_glyph_var_data (glyph); + hb_vector_t shared_indices; GlyphVarData::tuple_iterator_t iterator; if (!GlyphVarData::get_tuple_iterator (var_data, gvar_table->get_glyph_var_data_length (glyph), gvar_table->axisCount, + shared_indices, &iterator)) return false; @@ -421,25 +537,26 @@ struct gvar 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; + 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; + GlyphVarData::range_checker_t checker (p, 0, length); hb_vector_t private_indices; if (iterator.current_tuple->has_private_points () && - !unpack_points (p, private_indices, checker)) return false; + !GlyphVarData::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; + if (!GlyphVarData::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; + if (!GlyphVarData::unpack_deltas (p, y_deltas, checker)) + return false; for (unsigned int i = 0; i < num_deltas; i++) { @@ -448,7 +565,6 @@ struct gvar 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 */ @@ -460,22 +576,22 @@ struct gvar { if (deltas[i].flag) continue; /* search in both directions within the contour for a pair of referenced points */ - unsigned int pre; - for (pre = i;;) + unsigned int prev; + for (prev = i;;) { - if (pre-- <= start_point) pre = end_point; - if (pre == i || deltas[pre].flag) break; + if (prev-- <= start_point) prev = end_point; + if (prev == i || deltas[prev].flag) break; } - if (pre == i) continue; /* no (preceeding) referenced point was found */ - unsigned int fol; - for (fol = i;;) + if (prev == i) continue; /* no (previous) referenced point was found */ + unsigned int next; + for (next = i;;) { - if (fol++ >= end_point) fol = start_point; - if (fol == i || deltas[fol].flag) break; + if (next++ >= end_point) next = start_point; + if (next == i || deltas[next].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); + assert (next != i); + deltas[i].x = infer_delta (points[i].x, points[prev].x, points[next].x, deltas[prev].x, deltas[next].x); + deltas[i].y = infer_delta (points[i].y, points[prev].y, points[next].y, deltas[prev].y, deltas[next].y); } start_point = end_point + 1; } @@ -498,7 +614,8 @@ struct gvar hb_vector_t points; hb_vector_t end_points; 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; + 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_acc_t::PHANTOM_COUNT; i++) phantoms[i] = points[points.length - glyf_acc_t::PHANTOM_COUNT + i]; @@ -542,14 +659,15 @@ struct gvar 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; + 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]); + bounds.add (points[i]); /* TODO: need to check ON_CURVE or flatten? */ return true; } /* composite glyph */ @@ -622,100 +740,6 @@ struct gvar 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; diff --git a/test/api/Makefile.am b/test/api/Makefile.am index 67d66e16d..0f3dab73a 100644 --- a/test/api/Makefile.am +++ b/test/api/Makefile.am @@ -82,6 +82,7 @@ TEST_PROGS += \ test-ot-name \ test-ot-tag \ test-ot-extents-cff \ + test-ot-metrics-tt-var \ $(NULL) diff --git a/test/api/fonts/SourceSansVariable-Roman.abc.ttf b/test/api/fonts/SourceSansVariable-Roman.abc.ttf new file mode 100644 index 0000000000000000000000000000000000000000..690d7d5392e21dc9ad01b86dfbe35ce8591c02ac GIT binary patch literal 3240 zcma)8drVu`8UN0`#yG}+F@Asn#{*-)jU8i*c{$WLgsdq6$Ix_*)Rr6bFby`I@tVh^ z38G4^DpiWMO3Po{G;L9qKPK%VZHk(RYExECQ?+GLmQ~vnr4s5;l%h02T~*=z&bgOI zvS`h{I_G@P-}gA@K8O)fH6|Se4-Os}ip2dkB10I`eFqbxWBTu`-2`7CV#b4GFT}rR zJ8LJ}wFrIW>qiHYueF^^f&VS%4}*W^UP}-7HSp2VvAv;VFD+aklHK4Z#>NN6%Z-0s zAu{d(|0m^jC4btLfp6J2zVm_ zGtiHmoBSvIVb5CAQ_7_6SF4waq+i26l7_(eX2}QOzXb0|X9}kb0Tu#(7rc3HKBdsd z^g8&9;EfsORF3MI1pY1X?yQoT`hoN6e&ll-4GQJv7YfC{5c~~Rt|y72#Ru$Nwn`1u zO5|wtdOO3Bu87y$)Tl3Q>6bb}cDt>rsijG;ZwW=ZIx8fb)qbt{S?~EDv#M~YBk3?# zh6h`^V@{_|YJMrOCmEdmc4WX**W*u&Sz3nMTP-zZ4faqnxberBD^Txi{kf?v+Sauf zDbfUDuSnO)N=-y1joy}~(x$2oTWKUB!dgYlNM~5hrpj(-md3+PlFsRpyf69(Uhwrz zbcGWE_rbpAalbEN`dVF3x_)!Q9f)QR9a-qF?@RCBcf9ABwvAgJ7urh^#QTY;l}F>1 z@<5JKfv-nl_72qO(Ce9LpVA#wdP4CUYfZ1WBiZgxcC`)GT3pYY`f{U*Z}v4i?2g?% zQ}Os@cVmsct{L6r=TJxI$V6Ex6&wWGr6XBT{n)FYg5tKUWcM#V`~J(XRz@F^EPulF zfBXDMwYD~FL^p05ax&jnJ$fZBDMu4*g`$!Z>s5+y9QoG9r`XT)u#XyYkHzH~$!v{2 zpb|9vZHb@K=hcNM*7t>tspNLC5pjx&{X0qhJ(DN%x9+9*I@CiT#ipuzyMK zVsVSQxF@gAgTd)Ra2&~l)hKu!EV)i69$5&$tW8% z+D1OvLO(;tLKLKS3^)0=xNcAbG)ePxoTfmJ(P>(s0_Y4@H$8{bmx4A=IWa3jHwjHE zr9l@U@1?!CPl`K${oO@*;5HgUu z-8IN&p1uXE6ivBnDX^1=GU)ibOag`ldj57dYwHBKAkI8dH^NFP3bmLd^CoTtS#RSCtu`f!Yjg;7qR5NnNW33vPgHIbl`RSvF{`}@QTUNw zctkHeq8Fa6UI;I3;(q+yzVqU;|Lr~hra+Sj`J5_+*?=1A7Yg}#&~z$2QxKXb`}6)n zQ?BkO_vCT;Zg@yKIg>8n8$h!KWsVtOXNI&J{hq)R`4*iQ*7$eCHISPMbyO6cWnUyhoVR^Q!oK;}vIaV?=0`SbL7@h?lgHbc*pZy@3j9n;rC(`<6<=zKD)Hj>gHlRd9TnLF=+a1#FrHZw+&nTTf(*5C$f0ucO(@PA0^CAZSS848lb~u?B`skx{ zdivP@uJ&+mZ*Nbp<^J!1a`}$FW;mTb>>}UNS*F)7)vv5LLq6bEo68N1a=F6|#`Qm3 zxNzb9n?o<>mhht`aQ6duw^{!Xx&O!Fu50wJ-naPnDQC`{nTdJK5AV4g268SfIc0;X evKnZ+UDvK$ym;| + +/* Unit tests for glyph advance widths and extents of TrueType variable fonts */ + +static void +test_extents_tt_var (void) +{ + hb_face_t *face = hb_test_open_font_file ("fonts/SourceSansVariable-Roman.abc.ttf"); + g_assert (face); + hb_font_t *font = hb_font_create (face); + hb_face_destroy (face); + g_assert (font); + hb_ot_font_set_funcs (font); + + hb_glyph_extents_t extents; + hb_bool_t result = hb_font_get_glyph_extents (font, 1, &extents); + g_assert (result); + + g_assert_cmpint (extents.x_bearing, ==, 60); + g_assert_cmpint (extents.y_bearing, ==, 490); + g_assert_cmpint (extents.width, ==, 344); + g_assert_cmpint (extents.height, ==, -502); + + float coords[1] = { 500.0f }; + hb_font_set_var_coords_design (font, coords, 1); + result = hb_font_get_glyph_extents (font, 1, &extents); + g_assert (result); + + g_assert_cmpint (extents.x_bearing, ==, 49); + g_assert_cmpint (extents.y_bearing, ==, 501); + g_assert_cmpint (extents.width, ==, 393); + g_assert_cmpint (extents.height, ==, -513); + + hb_font_destroy (font); +} + +int +main (int argc, char **argv) +{ + hb_test_init (&argc, &argv); + + hb_test_add (test_extents_tt_var); + + return hb_test_run (); +} From 00b2653ac3b927f93ac350dbe1d3711790a50119 Mon Sep 17 00:00:00 2001 From: Michiharu Ariza Date: Sat, 16 Mar 2019 16:27:33 -0700 Subject: [PATCH 054/101] add components transformation --- src/hb-ot-glyf-table.hh | 44 +++++++++++++++++++++++++++++++++++-- src/hb-ot-var-gvar-table.hh | 31 +++++++++++++++----------- 2 files changed, 60 insertions(+), 15 deletions(-) diff --git a/src/hb-ot-glyf-table.hh b/src/hb-ot-glyf-table.hh index a494bbebb..2793fbaa3 100644 --- a/src/hb-ot-glyf-table.hh +++ b/src/hb-ot-glyf-table.hh @@ -172,6 +172,44 @@ struct glyf return size; } + void transform_point (float &x, float &y) const + { + int tx, ty; + const HBINT8 *p = &StructAfter (glyphIndex); + if (flags & ARG_1_AND_2_ARE_WORDS) + { + tx = *(const HBINT16 *)p; + p += HBINT16::static_size; + ty = *(const HBINT16 *)p; + p += HBINT16::static_size; + } + else + { + tx = *p++; + ty = *p++; + } + if (!(flags & ARGS_ARE_XY_VALUES)) tx = ty = 0; /* TODO: anchor point unsupported for now */ + + if (flags & WE_HAVE_A_SCALE) + { + float scale = ((const F2DOT14*)p)->to_float (); + x *= scale; + y *= scale; + } + else if (flags & WE_HAVE_AN_X_AND_Y_SCALE) + { + x *= ((const F2DOT14*)p)[0].to_float (); + y *= ((const F2DOT14*)p)[1].to_float (); + } + else if (flags & WE_HAVE_A_TWO_BY_TWO) + { + float x_ = x * ((const F2DOT14*)p)[0].to_float () + y * ((const F2DOT14*)p)[1].to_float (); + y = x * ((const F2DOT14*)p)[2].to_float () + y * ((const F2DOT14*)p)[3].to_float (); + x = x_; + } + if (tx | ty) { x += tx; y += ty; } + } + struct Iterator { const char *glyph_start; @@ -341,7 +379,8 @@ struct glyf uint8_t flag = points_[i].flag; if (coord_setter.is_short (flag)) { - if (unlikely (!checker.in_range (p))) return false; + if (unlikely (!checker.in_range (p))) + return false; if (coord_setter.is_same (flag)) v += *p++; else @@ -349,9 +388,10 @@ struct glyf } else { - if (unlikely (!checker.in_range ((const HBUINT16 *)p))) return false; if (!coord_setter.is_same (flag)) { + if (unlikely (!checker.in_range ((const HBUINT16 *)p))) + return false; v += *(const HBINT16 *)p; p += HBINT16::static_size; } diff --git a/src/hb-ot-var-gvar-table.hh b/src/hb-ot-var-gvar-table.hh index 5b5a7884b..43841274d 100644 --- a/src/hb-ot-var-gvar-table.hh +++ b/src/hb-ot-var-gvar-table.hh @@ -119,9 +119,9 @@ struct TupleVarHeader 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; } + bool has_peak () const { return (tupleIndex & TuppleIndex::EmbeddedPeakTuple); } + bool has_intermediate () const { return (tupleIndex & TuppleIndex::IntermediateRegion); } + bool has_private_points () const { return (tupleIndex & TuppleIndex::PrivatePointNumbers); } unsigned int get_index () const { return (tupleIndex & TuppleIndex::TupleIndexMask); } protected: @@ -144,7 +144,7 @@ struct TupleVarHeader struct TupleVarCount : HBUINT16 { - bool has_shared_point_numbers () const { return ((*this) & SharedPointNumbers) != 0; } + bool has_shared_point_numbers () const { return ((*this) & SharedPointNumbers); } unsigned int get_count () const { return (*this) & CountMask; } protected: @@ -249,7 +249,7 @@ struct GlyphVarData if (!check.in_range (p)) return false; uint16_t count = *p++; - if ((count & POINTS_ARE_WORDS) != 0) + if (count & POINTS_ARE_WORDS) { if (!check.in_range (p)) return false; count = ((count & POINT_RUN_COUNT_MASK) << 8) | *p++; @@ -263,7 +263,7 @@ struct GlyphVarData uint16_t j; uint8_t control = *p++; uint16_t run_count = (control & POINT_RUN_COUNT_MASK) + 1; - if ((control & POINTS_ARE_WORDS) != 0) + if (control & POINTS_ARE_WORDS) { for (j = 0; j < run_count && i < count; j++, i++) { @@ -304,12 +304,12 @@ struct GlyphVarData uint16_t j; uint8_t control = *p++; uint16_t run_count = (control & DELTA_RUN_COUNT_MASK) + 1; - if ((control & DELTAS_ARE_ZERO) != 0) + if (control & DELTAS_ARE_ZERO) { for (j = 0; j < run_count && i < count; j++, i++) deltas[i] = 0; } - else if ((control & DELTAS_ARE_WORDS) != 0) + else if (control & DELTAS_ARE_WORDS) { for (j = 0; j < run_count && i < count; j++, i++) { @@ -518,6 +518,7 @@ struct gvar if (unlikely (coord_count != gvar_table->axisCount)) return false; const GlyphVarData *var_data = gvar_table->get_glyph_var_data (glyph); + if (var_data == &Null(GlyphVarData)) return true; hb_vector_t shared_indices; GlyphVarData::tuple_iterator_t iterator; if (!GlyphVarData::get_tuple_iterator (var_data, @@ -624,10 +625,13 @@ struct gvar if (!glyf_accel.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; + if (composite.current->flags & glyf::CompositeGlyphHeader::USE_MY_METRICS) + { + if (!get_var_metrics (composite.current->glyphIndex, coords, coord_count, phantoms)) + return false; + for (unsigned int j = 0; j < phantoms.length; j++) + composite.current->transform_point (phantoms[j].x, phantoms[j].y); + } } while (composite.move_to_next()); return true; } @@ -677,7 +681,8 @@ struct gvar if (!get_bounds_var (composite.current->glyphIndex, coords, coord_count, comp_bounds)) return false; - /* TODO: support component scale/transformation */ + composite.current->transform_point (comp_bounds.min.x, comp_bounds.min.y); + composite.current->transform_point (comp_bounds.max.x, comp_bounds.max.y); bounds._union (comp_bounds); } while (composite.move_to_next()); return true; From 8a7998fd6ce730dd0f182d69d598b802476250dc Mon Sep 17 00:00:00 2001 From: Michiharu Ariza Date: Sun, 17 Mar 2019 15:36:26 -0700 Subject: [PATCH 055/101] moved most of var code from gvar to glyf initialize phantom points from metrics from htmx/vmtx & glyf bbox before execution added source file hb-ot-hmtx-table.cc to call glyf from hmtx/vmtx indirectly & temporarily, workaround a cyclic reference between the two --- src/Makefile.sources | 1 + src/hb-ot-face.hh | 1 - src/hb-ot-font.cc | 8 +- src/hb-ot-glyf-table.hh | 236 ++++++++++++++++++++++++++++++------ src/hb-ot-hmtx-table.cc | 61 ++++++++++ src/hb-ot-hmtx-table.hh | 39 +++--- src/hb-ot-var-gvar-table.hh | 201 +++++++----------------------- src/hb-ot-var-hvar-table.hh | 10 +- 8 files changed, 336 insertions(+), 221 deletions(-) create mode 100644 src/hb-ot-hmtx-table.cc diff --git a/src/Makefile.sources b/src/Makefile.sources index a8f609de4..6dc7427f6 100644 --- a/src/Makefile.sources +++ b/src/Makefile.sources @@ -68,6 +68,7 @@ HB_BASE_sources = \ hb-ot-head-table.hh \ hb-ot-hhea-table.hh \ hb-ot-hmtx-table.hh \ + hb-ot-hmtx-table.cc \ hb-ot-kern-table.hh \ hb-ot-layout-base-table.hh \ hb-ot-layout-common.hh \ diff --git a/src/hb-ot-face.hh b/src/hb-ot-face.hh index 9deb37798..7f47ba6cb 100644 --- a/src/hb-ot-face.hh +++ b/src/hb-ot-face.hh @@ -72,7 +72,6 @@ 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 742059ee7..d03371cc3 100644 --- a/src/hb-ot-font.cc +++ b/src/hb-ot-font.cc @@ -158,10 +158,10 @@ hb_ot_get_glyph_v_origin (hb_font_t *font, } hb_glyph_extents_t extents = {0}; - if (ot_face->glyf->get_extents (glyph, &extents)) + if (ot_face->glyf->get_extents (font, glyph, &extents)) { const OT::vmtx_accelerator_t &vmtx = *ot_face->vmtx; - hb_position_t tsb = vmtx.get_side_bearing (glyph); + hb_position_t tsb = vmtx.get_side_bearing (font, glyph); *y = font->em_scale_y (extents.y_bearing + tsb); return true; } @@ -183,9 +183,7 @@ 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); + ret = ot_face->glyf->get_extents (font, glyph, extents); if (!ret) ret = ot_face->cff1->get_extents (glyph, extents); if (!ret) diff --git a/src/hb-ot-glyf-table.hh b/src/hb-ot-glyf-table.hh index 2793fbaa3..a07a15630 100644 --- a/src/hb-ot-glyf-table.hh +++ b/src/hb-ot-glyf-table.hh @@ -30,6 +30,8 @@ #include "hb-open-type.hh" #include "hb-ot-head-table.hh" +#include "hb-ot-hmtx-table.hh" +#include "hb-ot-var-gvar-table.hh" #include "hb-subset-glyf.hh" namespace OT { @@ -222,8 +224,7 @@ struct glyf { const CompositeGlyphHeader *possible = &StructAfter (*current); - if (!in_range (possible)) - return false; + if (unlikely (!in_range (possible))) return false; current = possible; return true; } @@ -281,12 +282,19 @@ struct glyf glyf_table = hb_sanitize_context_t ().reference_table (face); num_glyphs = MAX (1u, loca_table.get_length () / (short_offset ? 2 : 4)) - 1; + + gvar_accel.init (face); + hmtx_accel.init (face); + vmtx_accel.init (face); } void fini () { loca_table.destroy (); glyf_table.destroy (); + gvar_accel.fini (); + hmtx_accel.fini (); + vmtx_accel.fini (); } /* @@ -328,30 +336,15 @@ struct glyf PHANTOM_COUNT = 4 }; - struct contour_point_t + protected: + const GlyphHeader &get_header (hb_codepoint_t glyph) const { - void init () { flag = 0; x = y = 0.0f; } - uint8_t flag; - float x, y; - }; + unsigned int start_offset, end_offset; + if (!get_offsets (glyph, &start_offset, &end_offset) || end_offset - start_offset < GlyphHeader::static_size) + return Null(GlyphHeader); - 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; - }; + return StructAtOffset (glyf_table, start_offset); + } struct x_setter_t { @@ -379,8 +372,7 @@ struct glyf uint8_t flag = points_[i].flag; if (coord_setter.is_short (flag)) { - if (unlikely (!checker.in_range (p))) - return false; + if (unlikely (!checker.in_range (p))) return false; if (coord_setter.is_same (flag)) v += *p++; else @@ -390,8 +382,7 @@ struct glyf { if (!coord_setter.is_same (flag)) { - if (unlikely (!checker.in_range ((const HBUINT16 *)p))) - return false; + if (unlikely (!checker.in_range ((const HBUINT16 *)p))) return false; v += *(const HBINT16 *)p; p += HBINT16::static_size; } @@ -401,23 +392,35 @@ struct glyf return true; } + void init_phantom_points (hb_codepoint_t glyph, hb_array_t &phantoms /* IN/OUT */) const + { + const GlyphHeader &header = get_header (glyph); + int h_delta = (int)header.xMin - hmtx_accel.get_side_bearing (glyph); + int v_delta = (int)header.yMax - vmtx_accel.get_side_bearing (glyph); + unsigned int h_adv = hmtx_accel.get_advance (glyph); + unsigned int v_adv = vmtx_accel.get_advance (glyph); + + phantoms[PHANTOM_LEFT].x = h_delta; + phantoms[PHANTOM_RIGHT].x = h_adv + h_delta; + phantoms[PHANTOM_TOP].y = v_delta; + phantoms[PHANTOM_BOTTOM].y = -v_adv + v_delta; + } + /* 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, hb_vector_t &points_ /* OUT */, - hb_vector_t &_end_points /* OUT */, + hb_vector_t &end_points_ /* OUT */, const bool phantom_only=false) 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; + if (unlikely (!get_offsets (glyph, &start_offset, &end_offset))) return false; + if (unlikely (end_offset - start_offset < GlyphHeader::static_size)) return false; - glyf::CompositeGlyphHeader::Iterator composite; + CompositeGlyphHeader::Iterator composite; if (get_composite (glyph, &composite)) { /* For a composite glyph, add one pseudo point for each component */ @@ -441,7 +444,7 @@ struct glyf else if (num_contours < 0) { CompositeGlyphHeader::Iterator composite; - if (!get_composite (glyph, &composite)) return false; + if (unlikely (!get_composite (glyph, &composite))) return false; do { num_points++; @@ -453,10 +456,10 @@ struct glyf if ((num_contours <= 0) || phantom_only) return true; /* Read simple glyph points if !phantom_only */ - _end_points.resize (num_contours); + end_points_.resize (num_contours); for (int16_t i = 0; i < num_contours; i++) - _end_points[i] = end_pts[i]; + end_points_[i] = end_pts[i]; /* Skip instructions */ const HBUINT8 *p = &StructAtOffset (&end_pts[num_contours+1], end_pts[num_contours]); @@ -481,6 +484,126 @@ struct glyf read_points (p, points_, checker)); } + /* 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 /* OUT */) const + { + hb_vector_t points; + hb_vector_t end_points; + if (unlikely (!get_contour_points (glyph, points, end_points, true/*phantom_only*/))) return false; + hb_array_t phantoms_array = points.sub_array (points.length-PHANTOM_COUNT, PHANTOM_COUNT); + init_phantom_points (glyph, phantoms_array); + if (unlikely (!gvar_accel.apply_deltas_to_points (glyph, coords, coord_count, points.as_array (), end_points.as_array ()))) return false; + + for (unsigned int i = 0; i < PHANTOM_COUNT; i++) + phantoms[i] = points[points.length - PHANTOM_COUNT + i]; + + CompositeGlyphHeader::Iterator composite; + if (!get_composite (glyph, &composite)) return true; /* simple glyph */ + do + { + if (composite.current->flags & CompositeGlyphHeader::USE_MY_METRICS) + { + if (unlikely (!get_var_metrics (composite.current->glyphIndex, coords, coord_count, phantoms))) return false; + for (unsigned int j = 0; j < phantoms.length; j++) + composite.current->transform_point (phantoms[j].x, phantoms[j].y); + } + } while (composite.move_to_next()); + 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 (unlikely (!get_contour_points (glyph, points, end_points))) return false; + hb_array_t phantoms_array = points.sub_array (points.length-PHANTOM_COUNT, PHANTOM_COUNT); + init_phantom_points (glyph, phantoms_array); + if (unlikely (!gvar_accel.apply_deltas_to_points (glyph, coords, coord_count, points.as_array (), end_points.as_array ()))) return false; + + CompositeGlyphHeader::Iterator composite; + if (!get_composite (glyph, &composite)) + { + /* simple glyph */ + for (unsigned int i = 0; i + PHANTOM_COUNT < points.length; i++) + bounds.add (points[i]); /* TODO: need to check ON_CURVE or flatten? */ + return true; + } + /* composite glyph */ + do + { + bounds_t comp_bounds; + if (unlikely (!get_bounds_var (composite.current->glyphIndex, coords, coord_count, comp_bounds))) return false; + + composite.current->transform_point (comp_bounds.min.x, comp_bounds.min.y); + composite.current->transform_point (comp_bounds.max.x, comp_bounds.max.y); + bounds._union (comp_bounds); + } while (composite.move_to_next()); + + /* Shift bounds by the updated left side bearing (vertically too?) */ + { + float x_delta = points[points.length - PHANTOM_COUNT + PHANTOM_LEFT].x; + bounds.min.x -= x_delta; + bounds.max.x -= x_delta; + } + + return true; + } + + bool get_extents_var (hb_codepoint_t glyph, + const int *coords, unsigned int coord_count, + hb_glyph_extents_t *extents) const + { + 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; + } + + public: /* based on FontTools _g_l_y_f.py::trim */ bool remove_padding (unsigned int start_offset, unsigned int *end_offset) const @@ -640,8 +763,40 @@ struct glyf return true; } - bool get_extents (hb_codepoint_t glyph, hb_glyph_extents_t *extents) const + 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_accel.get_axis_count ()) return advance; + + hb_vector_t phantoms; + phantoms.resize (PHANTOM_COUNT); + + if (unlikely (!get_var_metrics (glyph, coords, coord_count, phantoms))) return advance; + + if (vertical) + return -(phantoms[PHANTOM_BOTTOM].y - phantoms[PHANTOM_TOP].y); // is this sign correct? + else + return phantoms[PHANTOM_RIGHT].x - phantoms[PHANTOM_LEFT].x; + } + + int get_side_bearing_var (hb_codepoint_t glyph, const int *coords, unsigned int coord_count, bool vertical) const + { + hb_vector_t phantoms; + phantoms.resize (PHANTOM_COUNT); + + if (unlikely (!get_var_metrics (glyph, coords, coord_count, phantoms))) return 0; + return (int)(vertical? -ceilf (phantoms[PHANTOM_TOP].y): floorf (phantoms[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 > 0 && coord_count == gvar_accel.get_axis_count ()) + return get_extents_var (glyph, coords, coord_count, extents); + unsigned int start_offset, end_offset; if (!get_offsets (glyph, &start_offset, &end_offset)) return false; @@ -664,6 +819,11 @@ struct glyf unsigned int num_glyphs; hb_blob_ptr_t loca_table; hb_blob_ptr_t glyf_table; + + /* variable font support */ + gvar::accelerator_t gvar_accel; + hmtx::accelerator_t hmtx_accel; + vmtx::accelerator_t vmtx_accel; }; protected: diff --git a/src/hb-ot-hmtx-table.cc b/src/hb-ot-hmtx-table.cc new file mode 100644 index 000000000..9a58c1a51 --- /dev/null +++ b/src/hb-ot-hmtx-table.cc @@ -0,0 +1,61 @@ +/* + * 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 + */ + +#include "hb-ot-hmtx-table.hh" +#include "hb-ot-glyf-table.hh" + +namespace OT { + +template +int hmtxvmtx::accelerator_t::get_side_bearing_var_tt (hb_font_t *font, hb_codepoint_t glyph) const +{ + glyf::accelerator_t glyf_accel; + glyf_accel.init (font->face); + + int side_bearing = glyf_accel.get_side_bearing_var (glyph, font->coords, font->num_coords, T::tableTag==HB_OT_TAG_vmtx); + glyf_accel.fini (); + + return side_bearing; +} + +template +unsigned int hmtxvmtx::accelerator_t::get_advance_var_tt (hb_font_t *font, hb_codepoint_t glyph) const +{ + glyf::accelerator_t glyf_accel; + glyf_accel.init (font->face); + + unsigned int advance = glyf_accel.get_advance_var (glyph, font->coords, font->num_coords, T::tableTag==HB_OT_TAG_vmtx); + glyf_accel.fini (); + + return advance; +} + +template int hmtxvmtx::accelerator_t::get_side_bearing_var_tt (hb_font_t *font, hb_codepoint_t glyph) const; +template int hmtxvmtx::accelerator_t::get_side_bearing_var_tt (hb_font_t *font, hb_codepoint_t glyph) const; +template unsigned int hmtxvmtx::accelerator_t::get_advance_var_tt (hb_font_t *font, hb_codepoint_t glyph) const; +template unsigned int hmtxvmtx::accelerator_t::get_advance_var_tt (hb_font_t *font, hb_codepoint_t glyph) const; + +} diff --git a/src/hb-ot-hmtx-table.hh b/src/hb-ot-hmtx-table.hh index 4c1693f48..ff79199b0 100644 --- a/src/hb-ot-hmtx-table.hh +++ b/src/hb-ot-hmtx-table.hh @@ -31,7 +31,6 @@ #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 @@ -115,7 +114,7 @@ struct hmtxvmtx bool failed = false; for (unsigned int i = 0; i < num_output_glyphs; i++) { - unsigned int side_bearing = 0; + int side_bearing = 0; unsigned int advance = 0; hb_codepoint_t old_gid; if (plan->old_gid_for_new_gid (i, &old_gid)) @@ -208,28 +207,16 @@ 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. */ - unsigned int get_side_bearing (hb_codepoint_t glyph) const + int get_side_bearing (hb_codepoint_t glyph) const { if (glyph < num_advances) return table->longMetricZ[glyph].sb; @@ -241,6 +228,22 @@ struct hmtxvmtx return bearings[glyph - num_advances]; } + int get_side_bearing (hb_font_t *font, hb_codepoint_t glyph) const + { + int side_bearing = get_side_bearing (glyph); + if (likely (glyph < num_metrics)) + { + if (font->num_coords) + { + if (var_table.get_blob () != hb_blob_get_empty ()) + side_bearing += var_table->get_side_bearing_var (glyph, font->coords, font->num_coords); // TODO Optimize?! + else + side_bearing = get_side_bearing_var_tt (font, glyph); + } + } + return side_bearing; + } + unsigned int get_advance (hb_codepoint_t glyph) const { if (unlikely (glyph >= num_metrics)) @@ -268,7 +271,7 @@ struct hmtxvmtx 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); + advance = get_advance_var_tt (font, glyph); } } return advance; @@ -290,6 +293,9 @@ struct hmtxvmtx } private: + int get_side_bearing_var_tt (hb_font_t *font, hb_codepoint_t glyph) const; + unsigned int get_advance_var_tt (hb_font_t *font, hb_codepoint_t glyph) const; + unsigned int _advance_for_new_gid (const hb_subset_plan_t *plan, hb_codepoint_t new_gid) const { @@ -314,7 +320,6 @@ 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 43841274d..0a8a4909a 100644 --- a/src/hb-ot-var-gvar-table.hh +++ b/src/hb-ot-var-gvar-table.hh @@ -41,6 +41,31 @@ namespace OT { +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 Tuple : UnsizedArrayOf {}; struct TuppleIndex : HBUINT16 @@ -159,8 +184,6 @@ struct TupleVarCount : HBUINT16 struct GlyphVarData { - typedef glyf::accelerator_t::range_checker_t range_checker_t; - const TupleVarHeader &get_tuple_var_header (void) const { return StructAfter(data); } @@ -461,8 +484,6 @@ struct gvar 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; - public: struct accelerator_t { @@ -471,7 +492,6 @@ struct gvar memset (this, 0, sizeof (accelerator_t)); gvar_table = hb_sanitize_context_t ().reference_table (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 (); @@ -489,27 +509,8 @@ struct gvar void fini () { gvar_table.destroy (); - 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, @@ -541,7 +542,7 @@ struct gvar if (unlikely (!iterator.in_range (p, length))) return false; - GlyphVarData::range_checker_t checker (p, 0, length); + range_checker_t checker (p, 0, length); hb_vector_t private_indices; if (iterator.current_tuple->has_private_points () && !GlyphVarData::unpack_points (p, private_indices, checker)) @@ -607,148 +608,30 @@ struct gvar 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_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_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 */ - do - { - if (composite.current->flags & glyf::CompositeGlyphHeader::USE_MY_METRICS) - { - if (!get_var_metrics (composite.current->glyphIndex, coords, coord_count, phantoms)) - return false; - for (unsigned int j = 0; j < phantoms.length; j++) - composite.current->transform_point (phantoms[j].x, phantoms[j].y); - } - } while (composite.move_to_next()); - 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]); /* TODO: need to check ON_CURVE or flatten? */ - return true; - } - /* composite glyph */ - do - { - bounds_t comp_bounds; - if (!get_bounds_var (composite.current->glyphIndex, coords, coord_count, comp_bounds)) - return false; - - composite.current->transform_point (comp_bounds.min.x, comp_bounds.min.y); - composite.current->transform_point (comp_bounds.max.x, comp_bounds.max.y); - 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 - { - float advance = 0.f; - if (coord_count != gvar_table->axisCount) return advance; - - hb_vector_t points; - points.resize (glyf_acc_t::PHANTOM_COUNT); - - if (!get_var_metrics (glyph, coords, coord_count, points)) - return advance; - - if (vertical) - return -(points[glyf_acc_t::PHANTOM_BOTTOM].y - points[glyf_acc_t::PHANTOM_TOP].y); // is this sign correct? - else - 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; - } + unsigned int get_axis_count () const { return gvar_table->axisCount; } protected: + static float infer_delta (float target_val, float prev_val, float next_val, + float prev_delta, float next_delta) + { + if (prev_val == next_val) + return (prev_delta == next_delta)? prev_delta: 0.f; + else if (target_val <= MIN (prev_val, next_val)) + return (prev_val < next_val) ? prev_delta: next_delta; + else if (target_val >= MAX (prev_val, next_val)) + return (prev_val > next_val)? prev_delta: next_delta; + + /* linear interpolation */ + float r = (target_val - prev_val) / (next_val - prev_val); + return (1.f - r) * prev_delta + r * next_delta; + } + const GlyphVarData *get_glyph_var_data (hb_codepoint_t glyph) const { return gvar_table->get_glyph_var_data (glyph); } private: hb_blob_ptr_t gvar_table; hb_vector_t shared_tuples; - glyf::accelerator_t glyf_accel; }; protected: diff --git a/src/hb-ot-var-hvar-table.hh b/src/hb-ot-var-hvar-table.hh index 97e70605c..21d6b765b 100644 --- a/src/hb-ot-var-hvar-table.hh +++ b/src/hb-ot-var-hvar-table.hh @@ -359,7 +359,15 @@ struct HVARVVAR return (this+varStore).get_delta (varidx, coords, coord_count); } - bool has_sidebearing_deltas () const { return lsbMap && rsbMap; } + float get_side_bearing_var (hb_codepoint_t glyph, + const int *coords, unsigned int coord_count) const + { + if (!has_side_bearing_deltas ()) return 0.f; + unsigned int varidx = (this+lsbMap).map (glyph); + return (this+varStore).get_delta (varidx, coords, coord_count); + } + + bool has_side_bearing_deltas () const { return lsbMap && rsbMap; } protected: FixedVersion<>version; /* Version of the metrics variation table From c8b31773a6513e7051e5db98d7d26700856a32a3 Mon Sep 17 00:00:00 2001 From: Michiharu Ariza Date: Sun, 17 Mar 2019 16:01:49 -0700 Subject: [PATCH 056/101] added explicit casts to metrics --- src/hb-ot-hmtx-table.cc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/hb-ot-hmtx-table.cc b/src/hb-ot-hmtx-table.cc index 9a58c1a51..89c5fbc61 100644 --- a/src/hb-ot-hmtx-table.cc +++ b/src/hb-ot-hmtx-table.cc @@ -35,10 +35,10 @@ int hmtxvmtx::accelerator_t::get_side_bearing_var_tt (hb_font_t *font, hb_ glyf::accelerator_t glyf_accel; glyf_accel.init (font->face); - int side_bearing = glyf_accel.get_side_bearing_var (glyph, font->coords, font->num_coords, T::tableTag==HB_OT_TAG_vmtx); + float side_bearing = glyf_accel.get_side_bearing_var (glyph, font->coords, font->num_coords, T::tableTag==HB_OT_TAG_vmtx); glyf_accel.fini (); - return side_bearing; + return (int)side_bearing; } template @@ -47,10 +47,10 @@ unsigned int hmtxvmtx::accelerator_t::get_advance_var_tt (hb_font_t *font, glyf::accelerator_t glyf_accel; glyf_accel.init (font->face); - unsigned int advance = glyf_accel.get_advance_var (glyph, font->coords, font->num_coords, T::tableTag==HB_OT_TAG_vmtx); + float advance = glyf_accel.get_advance_var (glyph, font->coords, font->num_coords, T::tableTag==HB_OT_TAG_vmtx); glyf_accel.fini (); - return advance; + return (unsigned int)advance; } template int hmtxvmtx::accelerator_t::get_side_bearing_var_tt (hb_font_t *font, hb_codepoint_t glyph) const; From cabe22fc6967e6299c3a06bbfa9c19e9a23a0ae3 Mon Sep 17 00:00:00 2001 From: Michiharu Ariza Date: Sun, 17 Mar 2019 17:48:10 -0700 Subject: [PATCH 057/101] fix infer_delta code cleanup --- src/hb-ot-glyf-table.hh | 22 ++++++++++------ src/hb-ot-var-gvar-table.hh | 52 +++++++++++++++++++++++++------------ 2 files changed, 49 insertions(+), 25 deletions(-) diff --git a/src/hb-ot-glyf-table.hh b/src/hb-ot-glyf-table.hh index a07a15630..49a8b24ab 100644 --- a/src/hb-ot-glyf-table.hh +++ b/src/hb-ot-glyf-table.hh @@ -513,9 +513,9 @@ struct glyf return true; } - struct bounds_t + struct contour_bounds_t { - bounds_t () { min.x = min.y = FLT_MAX; max.x = max.y = FLT_MIN; } + contour_bounds_t () { min.x = min.y = FLT_MAX; max.x = max.y = FLT_MIN; } void add (const contour_point_t &p) { @@ -525,8 +525,14 @@ struct glyf max.y = MAX (max.y, p.y); } - void _union (const bounds_t &b) - { add (b.min); add (b.max); } + void merge (const contour_bounds_t &b) + { + if (empty ()) { *this = b; return; } + add (b.min); + add (b.max); + } + + bool empty () const { return (min.x >= max.x) || (min.y >= max.y); } contour_point_t min; contour_point_t max; @@ -535,7 +541,7 @@ struct glyf /* 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 + contour_bounds_t &bounds) const { hb_vector_t points; hb_vector_t end_points; @@ -555,12 +561,12 @@ struct glyf /* composite glyph */ do { - bounds_t comp_bounds; + contour_bounds_t comp_bounds; if (unlikely (!get_bounds_var (composite.current->glyphIndex, coords, coord_count, comp_bounds))) return false; composite.current->transform_point (comp_bounds.min.x, comp_bounds.min.y); composite.current->transform_point (comp_bounds.max.x, comp_bounds.max.y); - bounds._union (comp_bounds); + bounds.merge (comp_bounds); } while (composite.move_to_next()); /* Shift bounds by the updated left side bearing (vertically too?) */ @@ -577,7 +583,7 @@ struct glyf const int *coords, unsigned int coord_count, hb_glyph_extents_t *extents) const { - bounds_t bounds; + contour_bounds_t bounds; if (unlikely (!get_bounds_var (glyph, coords, coord_count, bounds))) return false; if (bounds.min.x >= bounds.max.x) diff --git a/src/hb-ot-var-gvar-table.hh b/src/hb-ot-var-gvar-table.hh index 0a8a4909a..490879718 100644 --- a/src/hb-ot-var-gvar-table.hh +++ b/src/hb-ot-var-gvar-table.hh @@ -511,6 +511,34 @@ struct gvar gvar_table.destroy (); } + private: + struct x_getter { static float get (const contour_point_t &p) { return p.x; } }; + struct y_getter { static float get (const contour_point_t &p) { return p.y; } }; + + template + static float infer_delta (const hb_array_t points, + const hb_array_t deltas, + unsigned int target, unsigned int prev, unsigned int next) + { + float target_val = T::get (points[target]); + float prev_val = T::get (points[prev]); + float next_val = T::get (points[next]); + float prev_delta = T::get (deltas[prev]); + float next_delta = T::get (deltas[next]); + + if (prev_val == next_val) + return (prev_delta == next_delta)? prev_delta: 0.f; + else if (target_val <= MIN (prev_val, next_val)) + return (prev_val < next_val) ? prev_delta: next_delta; + else if (target_val >= MAX (prev_val, next_val)) + return (prev_val > next_val)? prev_delta: next_delta; + + /* linear interpolation */ + float r = (target_val - prev_val) / (next_val - prev_val); + return (1.f - r) * prev_delta + r * next_delta; + } + + public: bool apply_deltas_to_points (hb_codepoint_t glyph, const int *coords, unsigned int coord_count, const hb_array_t points, @@ -570,6 +598,11 @@ struct gvar } while (iterator.move_to_next ()); /* infer deltas for unreferenced points */ + hb_vector_t orig_points; + orig_points.resize (points.length); + for (unsigned int i = 0; i < orig_points.length; i++) + orig_points[i] = points[i]; + unsigned int start_point = 0; for (unsigned int c = 0; c < end_points.length; c++) { @@ -592,8 +625,8 @@ struct gvar if (next == i || deltas[next].flag) break; } assert (next != i); - deltas[i].x = infer_delta (points[i].x, points[prev].x, points[next].x, deltas[prev].x, deltas[next].x); - deltas[i].y = infer_delta (points[i].y, points[prev].y, points[next].y, deltas[prev].y, deltas[next].y); + deltas[i].x = infer_delta (orig_points.as_array (), deltas.as_array (), i, prev, next); + deltas[i].y = infer_delta (orig_points.as_array (), deltas.as_array (), i, prev, next); } start_point = end_point + 1; } @@ -611,21 +644,6 @@ struct gvar unsigned int get_axis_count () const { return gvar_table->axisCount; } protected: - static float infer_delta (float target_val, float prev_val, float next_val, - float prev_delta, float next_delta) - { - if (prev_val == next_val) - return (prev_delta == next_delta)? prev_delta: 0.f; - else if (target_val <= MIN (prev_val, next_val)) - return (prev_val < next_val) ? prev_delta: next_delta; - else if (target_val >= MAX (prev_val, next_val)) - return (prev_val > next_val)? prev_delta: next_delta; - - /* linear interpolation */ - float r = (target_val - prev_val) / (next_val - prev_val); - return (1.f - r) * prev_delta + r * next_delta; - } - const GlyphVarData *get_glyph_var_data (hb_codepoint_t glyph) const { return gvar_table->get_glyph_var_data (glyph); } From 597ad4df0cdff4a0355121e3da2a59e7fa8ee68f Mon Sep 17 00:00:00 2001 From: Michiharu Ariza Date: Sun, 17 Mar 2019 18:45:30 -0700 Subject: [PATCH 058/101] fix unpack_points --- src/hb-ot-var-gvar-table.hh | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/hb-ot-var-gvar-table.hh b/src/hb-ot-var-gvar-table.hh index 490879718..969885915 100644 --- a/src/hb-ot-var-gvar-table.hh +++ b/src/hb-ot-var-gvar-table.hh @@ -279,6 +279,7 @@ struct GlyphVarData } points.resize (count); + unsigned int n = 0; uint16_t i = 0; while (i < count) { @@ -291,7 +292,8 @@ struct GlyphVarData for (j = 0; j < run_count && i < count; j++, i++) { if (!check.in_range ((const HBUINT16 *)p)) return false; - points[i] = *(const HBUINT16 *)p; + n += *(const HBUINT16 *)p; + points[i] = n; p += HBUINT16::static_size; } } @@ -300,7 +302,8 @@ struct GlyphVarData for (j = 0; j < run_count && i < count; j++, i++) { if (!check.in_range (p)) return false; - points[i] = *p++; + n += *p++; + points[i] = n; } } if (j < run_count) return false; From b6cc838888cc302f0de19030b75773fe0fda372f Mon Sep 17 00:00:00 2001 From: Michiharu Ariza Date: Sun, 17 Mar 2019 22:49:18 -0700 Subject: [PATCH 059/101] fix composite glyf extents --- src/hb-ot-glyf-table.hh | 39 ++++++++++++++++++++++++------------- src/hb-ot-var-gvar-table.hh | 3 +++ 2 files changed, 29 insertions(+), 13 deletions(-) diff --git a/src/hb-ot-glyf-table.hh b/src/hb-ot-glyf-table.hh index 49a8b24ab..f60655fa8 100644 --- a/src/hb-ot-glyf-table.hh +++ b/src/hb-ot-glyf-table.hh @@ -487,14 +487,15 @@ struct glyf /* 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 /* OUT */) const + hb_array_t phantoms /* OUT */) const { hb_vector_t points; hb_vector_t end_points; if (unlikely (!get_contour_points (glyph, points, end_points, true/*phantom_only*/))) return false; hb_array_t phantoms_array = points.sub_array (points.length-PHANTOM_COUNT, PHANTOM_COUNT); init_phantom_points (glyph, phantoms_array); - if (unlikely (!gvar_accel.apply_deltas_to_points (glyph, coords, coord_count, points.as_array (), end_points.as_array ()))) return false; + if (unlikely (!gvar_accel.apply_deltas_to_points (glyph, coords, coord_count, + points.as_array (), end_points.as_array ()))) return false; for (unsigned int i = 0; i < PHANTOM_COUNT; i++) phantoms[i] = points[points.length - PHANTOM_COUNT + i]; @@ -505,7 +506,8 @@ struct glyf { if (composite.current->flags & CompositeGlyphHeader::USE_MY_METRICS) { - if (unlikely (!get_var_metrics (composite.current->glyphIndex, coords, coord_count, phantoms))) return false; + if (unlikely (!get_var_metrics (composite.current->glyphIndex, coords, coord_count, + phantoms.sub_array (0, 2)))) return false; for (unsigned int j = 0; j < phantoms.length; j++) composite.current->transform_point (phantoms[j].x, phantoms[j].y); } @@ -525,6 +527,8 @@ struct glyf max.y = MAX (max.y, p.y); } + void offset (const contour_point_t &p) { min.offset (p); max.offset (p); } + void merge (const contour_bounds_t &b) { if (empty ()) { *this = b; return; } @@ -550,24 +554,33 @@ struct glyf init_phantom_points (glyph, phantoms_array); if (unlikely (!gvar_accel.apply_deltas_to_points (glyph, coords, coord_count, points.as_array (), end_points.as_array ()))) return false; + unsigned int comp_index = 0; CompositeGlyphHeader::Iterator composite; if (!get_composite (glyph, &composite)) { /* simple glyph */ for (unsigned int i = 0; i + PHANTOM_COUNT < points.length; i++) bounds.add (points[i]); /* TODO: need to check ON_CURVE or flatten? */ - return true; } - /* composite glyph */ - do + else { - contour_bounds_t comp_bounds; - if (unlikely (!get_bounds_var (composite.current->glyphIndex, coords, coord_count, comp_bounds))) return false; + /* composite glyph */ + do + { + contour_bounds_t comp_bounds; + if (unlikely (!get_bounds_var (composite.current->glyphIndex, coords, coord_count, comp_bounds))) return false; - composite.current->transform_point (comp_bounds.min.x, comp_bounds.min.y); - composite.current->transform_point (comp_bounds.max.x, comp_bounds.max.y); - bounds.merge (comp_bounds); - } while (composite.move_to_next()); + /* Apply offset & scaling */ + composite.current->transform_point (comp_bounds.min.x, comp_bounds.min.y); + composite.current->transform_point (comp_bounds.max.x, comp_bounds.max.y); + + /* Apply offset adjustments from gvar */ + comp_bounds.offset (points[comp_index]); + + bounds.merge (comp_bounds); + comp_index++; + } while (composite.move_to_next()); + } /* Shift bounds by the updated left side bearing (vertically too?) */ { @@ -779,7 +792,7 @@ struct glyf hb_vector_t phantoms; phantoms.resize (PHANTOM_COUNT); - if (unlikely (!get_var_metrics (glyph, coords, coord_count, phantoms))) return advance; + if (unlikely (!get_var_metrics (glyph, coords, coord_count, phantoms.as_array ()))) return advance; if (vertical) return -(phantoms[PHANTOM_BOTTOM].y - phantoms[PHANTOM_TOP].y); // is this sign correct? diff --git a/src/hb-ot-var-gvar-table.hh b/src/hb-ot-var-gvar-table.hh index 969885915..721bb5fca 100644 --- a/src/hb-ot-var-gvar-table.hh +++ b/src/hb-ot-var-gvar-table.hh @@ -44,6 +44,9 @@ namespace OT { struct contour_point_t { void init () { flag = 0; x = y = 0.0f; } + + void offset (const contour_point_t &p) { x += p.x; y += p.y; } + uint8_t flag; float x, y; }; From 9d9d5c706b68a16b2d07f8b8972b2b499c94bf0a Mon Sep 17 00:00:00 2001 From: Michiharu Ariza Date: Mon, 18 Mar 2019 10:48:53 -0700 Subject: [PATCH 060/101] fix build --- src/hb-ot-hmtx-table.cc | 15 ++++----------- src/hb-ot-hmtx-table.hh | 15 +++++++++------ src/hb-ot-var-gvar-table.hh | 1 + 3 files changed, 14 insertions(+), 17 deletions(-) diff --git a/src/hb-ot-hmtx-table.cc b/src/hb-ot-hmtx-table.cc index 89c5fbc61..d686fe3b8 100644 --- a/src/hb-ot-hmtx-table.cc +++ b/src/hb-ot-hmtx-table.cc @@ -29,33 +29,26 @@ namespace OT { -template -int hmtxvmtx::accelerator_t::get_side_bearing_var_tt (hb_font_t *font, hb_codepoint_t glyph) const +int hmtxvmtx_accelerator_base_t::get_side_bearing_var_tt (hb_font_t *font, hb_codepoint_t glyph, bool vertical) { glyf::accelerator_t glyf_accel; glyf_accel.init (font->face); - float side_bearing = glyf_accel.get_side_bearing_var (glyph, font->coords, font->num_coords, T::tableTag==HB_OT_TAG_vmtx); + float side_bearing = glyf_accel.get_side_bearing_var (glyph, font->coords, font->num_coords, vertical); glyf_accel.fini (); return (int)side_bearing; } -template -unsigned int hmtxvmtx::accelerator_t::get_advance_var_tt (hb_font_t *font, hb_codepoint_t glyph) const +unsigned int hmtxvmtx_accelerator_base_t::get_advance_var_tt (hb_font_t *font, hb_codepoint_t glyph, bool vertical) { glyf::accelerator_t glyf_accel; glyf_accel.init (font->face); - float advance = glyf_accel.get_advance_var (glyph, font->coords, font->num_coords, T::tableTag==HB_OT_TAG_vmtx); + float advance = glyf_accel.get_advance_var (glyph, font->coords, font->num_coords, vertical); glyf_accel.fini (); return (unsigned int)advance; } -template int hmtxvmtx::accelerator_t::get_side_bearing_var_tt (hb_font_t *font, hb_codepoint_t glyph) const; -template int hmtxvmtx::accelerator_t::get_side_bearing_var_tt (hb_font_t *font, hb_codepoint_t glyph) const; -template unsigned int hmtxvmtx::accelerator_t::get_advance_var_tt (hb_font_t *font, hb_codepoint_t glyph) const; -template unsigned int hmtxvmtx::accelerator_t::get_advance_var_tt (hb_font_t *font, hb_codepoint_t glyph) const; - } diff --git a/src/hb-ot-hmtx-table.hh b/src/hb-ot-hmtx-table.hh index ff79199b0..08fa2c9cd 100644 --- a/src/hb-ot-hmtx-table.hh +++ b/src/hb-ot-hmtx-table.hh @@ -53,6 +53,12 @@ struct LongMetric DEFINE_SIZE_STATIC (4); }; +struct hmtxvmtx_accelerator_base_t +{ + HB_INTERNAL static int get_side_bearing_var_tt (hb_font_t *font, hb_codepoint_t glyph, bool vertical); + HB_INTERNAL static unsigned int get_advance_var_tt (hb_font_t *font, hb_codepoint_t glyph, bool vertical); +}; + template struct hmtxvmtx { @@ -156,7 +162,7 @@ struct hmtxvmtx return success; } - struct accelerator_t + struct accelerator_t : hmtxvmtx_accelerator_base_t { friend struct hmtxvmtx; @@ -238,7 +244,7 @@ struct hmtxvmtx if (var_table.get_blob () != hb_blob_get_empty ()) side_bearing += var_table->get_side_bearing_var (glyph, font->coords, font->num_coords); // TODO Optimize?! else - side_bearing = get_side_bearing_var_tt (font, glyph); + side_bearing = get_side_bearing_var_tt (font, glyph, T::tableTag==HB_OT_TAG_vmtx); } } return side_bearing; @@ -271,7 +277,7 @@ struct hmtxvmtx 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 = get_advance_var_tt (font, glyph); + advance = get_advance_var_tt (font, glyph, T::tableTag==HB_OT_TAG_vmtx); } } return advance; @@ -293,9 +299,6 @@ struct hmtxvmtx } private: - int get_side_bearing_var_tt (hb_font_t *font, hb_codepoint_t glyph) const; - unsigned int get_advance_var_tt (hb_font_t *font, hb_codepoint_t glyph) const; - unsigned int _advance_for_new_gid (const hb_subset_plan_t *plan, hb_codepoint_t new_gid) const { diff --git a/src/hb-ot-var-gvar-table.hh b/src/hb-ot-var-gvar-table.hh index 721bb5fca..77454dcd9 100644 --- a/src/hb-ot-var-gvar-table.hh +++ b/src/hb-ot-var-gvar-table.hh @@ -515,6 +515,7 @@ struct gvar void fini () { gvar_table.destroy (); + shared_tuples.fini (); } private: From 9d3a252030e8f86aff4a35ce56fe77eb718e2071 Mon Sep 17 00:00:00 2001 From: Michiharu Ariza Date: Mon, 18 Mar 2019 15:39:10 -0700 Subject: [PATCH 061/101] add api test for tt var advance widths stripped HVAR from SourceSansVariable-Roman.abc.ttf so glyf gets parsed --- src/hb-ot-hmtx-table.hh | 1 - .../fonts/SourceSansVariable-Roman.abc.ttf | Bin 3240 -> 3168 bytes test/api/test-ot-metrics-tt-var.c | 37 ++++++++++++++++++ 3 files changed, 37 insertions(+), 1 deletion(-) diff --git a/src/hb-ot-hmtx-table.hh b/src/hb-ot-hmtx-table.hh index 08fa2c9cd..d3594467c 100644 --- a/src/hb-ot-hmtx-table.hh +++ b/src/hb-ot-hmtx-table.hh @@ -221,7 +221,6 @@ struct hmtxvmtx var_table.destroy (); } - /* TODO Add variations version. */ int get_side_bearing (hb_codepoint_t glyph) const { if (glyph < num_advances) diff --git a/test/api/fonts/SourceSansVariable-Roman.abc.ttf b/test/api/fonts/SourceSansVariable-Roman.abc.ttf index 690d7d5392e21dc9ad01b86dfbe35ce8591c02ac..f2b141b48f41fdc00f46ccd83cd4d2d0b38c0909 100644 GIT binary patch delta 403 zcmZ1>`9PwcfsuiMK?q2&FetdYxVo7+>54KiaQy&^+PVk$2eZ|ExW>T1(ZayM$mJd! z>NHujNsNJka{^G_%s0$2Nc)|-C6Ipt$hYwi);EfZEM5jwy8_5h2o7-!;pMvg3}_w; zkbfqzEU~CkE_Vlze*wr>OU_NKFJO>n3SeMhk^pH)11f0$SIz;nPzA(Kq4vp84` zlm|J$D;+5BHzDON0|U1Okgt)EnwTQG`PD8UTL-A#ECVRO)y@iZ3}XtAuac2lQo*6e zXav*`G?PIfCqFrn;W0xQkY54hbLA#lRTMDDFfsu(bN~eu@)C1X=Sl5zW?*0jx{$@F zAiuce|3wC%0URJ7D1bwYkyZZOJnsC-2bgpw|6x*{+{vUcS&=zjfT@5Xf$hRrsNzKok6GKaBD{>_!Nxst<;ar1XhHpa=Z+-j4}Ih;1P Iaw{_e018cB*8l(j delta 469 zcmaDLu|l$*fsuiMK^RD|Fc`SIxVo7+>54KiaF_r^ZQTR>gW2jnTm$k;7#JA2+=D}% zCW|(SF)(m80Oid*!W@ILG$$kj`8z;-Um#!mow_BE{{hIi@ekHFii#{=#=yX=0OThG zhd74ta$SDLz`&&grWwz0!g5eiKse0`&vcGiqd{CZ>pPezl8%foTg+y;%lOfUBML z1d#s+$XCh8EvewpV>ANt-vIdnIr(eg}}Rke8U7 zI!|h!GtfZ~fI(zbkY8N#{~`m>01gq5{N@%WU&hIuj0)mF9)lgwprt?>6r_x-@*6)- zkDYA6s3X8sz>vWBjKPLU3P|r`Fq@phs5*HiWBgPmel5BocxTFH8~O{U*%HUY|X982mmI-W=a46 diff --git a/test/api/test-ot-metrics-tt-var.c b/test/api/test-ot-metrics-tt-var.c index f944f173a..13152509d 100644 --- a/test/api/test-ot-metrics-tt-var.c +++ b/test/api/test-ot-metrics-tt-var.c @@ -61,12 +61,49 @@ test_extents_tt_var (void) hb_font_destroy (font); } +static void +test_advance_tt_var (void) +{ + hb_face_t *face = hb_test_open_font_file ("fonts/SourceSansVariable-Roman.abc.ttf"); + g_assert (face); + hb_font_t *font = hb_font_create (face); + hb_face_destroy (face); + g_assert (font); + hb_ot_font_set_funcs (font); + + hb_position_t x, y; + hb_font_get_glyph_advance_for_direction(font, 1, HB_DIRECTION_LTR, &x, &y); + + g_assert_cmpint (x, ==, 486); + g_assert_cmpint (y, ==, 0); + + hb_font_get_glyph_advance_for_direction(font, 1, HB_DIRECTION_TTB, &x, &y); + + g_assert_cmpint (x, ==, 0); + g_assert_cmpint (y, ==, -1000); + + float coords[1] = { 500.0f }; + hb_font_set_var_coords_design (font, coords, 1); + hb_font_get_glyph_advance_for_direction(font, 1, HB_DIRECTION_LTR, &x, &y); + + g_assert_cmpint (x, ==, 510); + g_assert_cmpint (y, ==, 0); + + hb_font_get_glyph_advance_for_direction(font, 1, HB_DIRECTION_TTB, &x, &y); + + g_assert_cmpint (x, ==, 0); + g_assert_cmpint (y, ==, -1000); + + hb_font_destroy (font); +} + int main (int argc, char **argv) { hb_test_init (&argc, &argv); hb_test_add (test_extents_tt_var); + hb_test_add (test_advance_tt_var); return hb_test_run (); } From c7edd14dc96af59cb53e9560a45f48d809fe8bb1 Mon Sep 17 00:00:00 2001 From: Michiharu Ariza Date: Mon, 18 Mar 2019 17:16:43 -0700 Subject: [PATCH 062/101] fix empty glyf's advance width --- src/hb-ot-glyf-table.hh | 8 +++++++- src/hb-ot-hmtx-table.cc | 4 ++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/hb-ot-glyf-table.hh b/src/hb-ot-glyf-table.hh index f60655fa8..06a799d20 100644 --- a/src/hb-ot-glyf-table.hh +++ b/src/hb-ot-glyf-table.hh @@ -418,7 +418,13 @@ struct glyf unsigned int num_points = 0; unsigned int start_offset, end_offset; if (unlikely (!get_offsets (glyph, &start_offset, &end_offset))) return false; - if (unlikely (end_offset - start_offset < GlyphHeader::static_size)) return false; + if (unlikely (end_offset - start_offset < GlyphHeader::static_size)) + { + /* empty glyph */ + points_.resize (PHANTOM_COUNT); + for (unsigned int i = 0; i < points_.length; i++) points_[i].init (); + return true; + } CompositeGlyphHeader::Iterator composite; if (get_composite (glyph, &composite)) diff --git a/src/hb-ot-hmtx-table.cc b/src/hb-ot-hmtx-table.cc index d686fe3b8..69b0f365f 100644 --- a/src/hb-ot-hmtx-table.cc +++ b/src/hb-ot-hmtx-table.cc @@ -37,7 +37,7 @@ int hmtxvmtx_accelerator_base_t::get_side_bearing_var_tt (hb_font_t *font, hb_co float side_bearing = glyf_accel.get_side_bearing_var (glyph, font->coords, font->num_coords, vertical); glyf_accel.fini (); - return (int)side_bearing; + return (int)roundf (side_bearing); } unsigned int hmtxvmtx_accelerator_base_t::get_advance_var_tt (hb_font_t *font, hb_codepoint_t glyph, bool vertical) @@ -48,7 +48,7 @@ unsigned int hmtxvmtx_accelerator_base_t::get_advance_var_tt (hb_font_t *font, h float advance = glyf_accel.get_advance_var (glyph, font->coords, font->num_coords, vertical); glyf_accel.fini (); - return (unsigned int)advance; + return (unsigned int)roundf (advance); } } From 560bcd774473691b310e746d2e7c0287c2bea9fe Mon Sep 17 00:00:00 2001 From: Michiharu Ariza Date: Mon, 18 Mar 2019 17:50:20 -0700 Subject: [PATCH 063/101] move rounding advance width to glyf --- src/hb-ot-glyf-table.hh | 24 ++++++++++++++---------- src/hb-ot-hmtx-table.cc | 8 ++++---- 2 files changed, 18 insertions(+), 14 deletions(-) diff --git a/src/hb-ot-glyf-table.hh b/src/hb-ot-glyf-table.hh index 06a799d20..d4cae61b5 100644 --- a/src/hb-ot-glyf-table.hh +++ b/src/hb-ot-glyf-table.hh @@ -788,22 +788,24 @@ struct glyf return true; } - float get_advance_var (hb_codepoint_t glyph, - const int *coords, unsigned int coord_count, - bool vertical) const + unsigned int 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_accel.get_axis_count ()) return advance; - + bool success = false; hb_vector_t phantoms; phantoms.resize (PHANTOM_COUNT); - if (unlikely (!get_var_metrics (glyph, coords, coord_count, phantoms.as_array ()))) return advance; + if (likely (coord_count == gvar_accel.get_axis_count ())) + success = get_var_metrics (glyph, coords, coord_count, phantoms.as_array ()); + + if (unlikely (!success)) + return vertical? vmtx_accel.get_advance (glyph): hmtx_accel.get_advance (glyph); if (vertical) - return -(phantoms[PHANTOM_BOTTOM].y - phantoms[PHANTOM_TOP].y); // is this sign correct? + return (unsigned int)roundf (phantoms[PHANTOM_TOP].y - phantoms[PHANTOM_BOTTOM].y); else - return phantoms[PHANTOM_RIGHT].x - phantoms[PHANTOM_LEFT].x; + return (unsigned int)roundf (phantoms[PHANTOM_RIGHT].x - phantoms[PHANTOM_LEFT].x); } int get_side_bearing_var (hb_codepoint_t glyph, const int *coords, unsigned int coord_count, bool vertical) const @@ -811,7 +813,9 @@ struct glyf hb_vector_t phantoms; phantoms.resize (PHANTOM_COUNT); - if (unlikely (!get_var_metrics (glyph, coords, coord_count, phantoms))) return 0; + if (unlikely (!get_var_metrics (glyph, coords, coord_count, phantoms))) + return vertical? vmtx_accel.get_side_bearing (glyph): hmtx_accel.get_side_bearing (glyph); + return (int)(vertical? -ceilf (phantoms[PHANTOM_TOP].y): floorf (phantoms[PHANTOM_LEFT].x)); } diff --git a/src/hb-ot-hmtx-table.cc b/src/hb-ot-hmtx-table.cc index 69b0f365f..012a9f2d9 100644 --- a/src/hb-ot-hmtx-table.cc +++ b/src/hb-ot-hmtx-table.cc @@ -34,10 +34,10 @@ int hmtxvmtx_accelerator_base_t::get_side_bearing_var_tt (hb_font_t *font, hb_co glyf::accelerator_t glyf_accel; glyf_accel.init (font->face); - float side_bearing = glyf_accel.get_side_bearing_var (glyph, font->coords, font->num_coords, vertical); + int side_bearing = glyf_accel.get_side_bearing_var (glyph, font->coords, font->num_coords, vertical); glyf_accel.fini (); - return (int)roundf (side_bearing); + return side_bearing; } unsigned int hmtxvmtx_accelerator_base_t::get_advance_var_tt (hb_font_t *font, hb_codepoint_t glyph, bool vertical) @@ -45,10 +45,10 @@ unsigned int hmtxvmtx_accelerator_base_t::get_advance_var_tt (hb_font_t *font, h glyf::accelerator_t glyf_accel; glyf_accel.init (font->face); - float advance = glyf_accel.get_advance_var (glyph, font->coords, font->num_coords, vertical); + unsigned int advance = glyf_accel.get_advance_var (glyph, font->coords, font->num_coords, vertical); glyf_accel.fini (); - return (unsigned int)roundf (advance); + return advance; } } From aeddb30f87ea957e7e780705c099e849c6d7e27d Mon Sep 17 00:00:00 2001 From: Michiharu Ariza Date: Mon, 18 Mar 2019 18:11:00 -0700 Subject: [PATCH 064/101] replaced tt var api test with a composite glyph for better coverage --- .../SourceSansVariable-Roman-nohvar-41,C1.ttf | Bin 0 -> 4696 bytes .../fonts/SourceSansVariable-Roman.abc.ttf | Bin 3168 -> 0 bytes test/api/test-ot-metrics-tt-var.c | 36 +++++++++--------- 3 files changed, 18 insertions(+), 18 deletions(-) create mode 100644 test/api/fonts/SourceSansVariable-Roman-nohvar-41,C1.ttf delete mode 100644 test/api/fonts/SourceSansVariable-Roman.abc.ttf diff --git a/test/api/fonts/SourceSansVariable-Roman-nohvar-41,C1.ttf b/test/api/fonts/SourceSansVariable-Roman-nohvar-41,C1.ttf new file mode 100644 index 0000000000000000000000000000000000000000..dc237e7904d6137cb9343c7e1418782ac8b241da GIT binary patch literal 4696 zcmbVPduUu)8UN0`lX>MelUK8A%-%loShtxbY4gsOnV6K8%_iAQHdR?_Ze}K#Y39Xb z+9Zo;LJKRkE=3oNMd}}2q#{ajSCq0YS<%oxmQo+k{!v6kWRXy1(Xz;H;_o~6OeTq0 z8$03N^S#ga_?>eJ2_h<`Mbat#;&@>6xrYs1kbI1%cQ|;Y^-Dcj8u5r5R)z+%9IGvLgKDzKt@(&nb4S>YmzWzD#7 zLhpgJlLl#mvh;OYmewTP$cyCjZ>$`FM3LYQfk%FpOcN#Iip7h6Dj5k6*TTGpt}5R$ z?H!BPjZImFj*c?VtE^AOV_TWMCLEv;Og&6FR%nG~WHWWGA1DP zV4ga;aKg%{lIW;+zqf9GL&HHw$I*_C{rmKCU3m2gadUl942F-}{C->enf2eDnZJ5f zOfGA0d=fWqo;`c*l^Zu;;1(sHe4tgYe~7G+nOH~cV)7zt`O)%&dVPJvLA%|dceL%V zbg$K{9k^6kS7s}#tGs+d)Nv?rxi|6r^NHT|>{?9?x+i1kLt4G;g#(CYtcr~X+A2l$ zA)bZa(6O(oY1;Gl6*LQuh3bxypOC{>H|q@A&DW zv$OqSdri$GjVxX8oIH7Vb@gs{_wie|j!)9n$*UJhdN;wld~NmBu@!Pv)zlnn>HbPZ zMN<>`mlnmNM@vOFufMsx{O0wWbj{(owj>@|UOeeVtrk`nL^nvzqm=U~x$7Cbpg%a! z{g)-teaJ8K*YB_Y9oNP(>g#o;Wi2q>{MocLO=wPX^Fy@pKw8-73b2_Z#}0d&#C(_b zQzd6ST3urFI>2$qkO@6YpHZ}Ji%kLUvwkJFQ#GAev=<0`-J)@4*y-Dfb|a_nD!P!K zrt69>g8eTQT@0-LO3@|x97HMYqkBs3L5ChH+KYW3DY}eoI3qG=QBD;&8x~zb1;Roa z;dE4r=TMQb;WndH;a9YvM$N8hjY>41qIDY8dKHa(VAH06!rJRfZl`ALLq$7iML(cu z;7R|pqFq#N!_6)CEu>akE`G6Y*d~;`gpN5D740Rr<84JhMYWE1EgA)NIvyxmP_?r{ z(Hd1a4=Y-yDyOOo=c>v%uH*$&=8P-aPPNXjD%z3X3wL{w^Lt9}0)0c#Zp2slh(bE% zyshL#-?tW#n{SQ*=2sx_+SO3Ua&d zD7q5y?*~)oGNFhOG?TN&xS5HWlkv!CWcpm(%(VELTbq0MBiCbDu~E*ZX*88Elh6+L z4U9)Jv$0gtupFYPY$%mH&z!HNxvM8(&O}n#smOf9X!W)E+S}T?+CMFx40<{e!xcU- zpUs$qOy!NGx+$;ORlTcFl@yF5Vw0(OcxT9*yl)pB%lhJGXlAFA6w(jLz`1Rv7CZ(u zZE~HU0u-hcO;QAOkQQhbU-t+luo`p2De89_6D6m^dz(f4Ix4TQp2c5HYqv>s}Mzur|>P< z!oxBM%cRV598?C;O;izwFUOyTXI#o#;Mpvz??J8o)JMbAvn^IG(#|+dh?GJ$ypQEO z4DSINNBj(Sj>)VIbTz+26tb+WJc;UZI-l%(7ou>q8Thl+6xMk>JR2Xi;c3Uajk>UI zt#vQ?ZM8R$8Bfo|lSQUH6P_q{X$Gi$IuIo8kKp!434L2O)&_{LWs3{68j#( zZW+0!<-;^1>Kr^dw-|h9QTdaga0I*YlnqSPhxbVpb??Z`0`F;LFot@pp5>9q9i2js zbFyzdCCeg?6^KOsjA2GE*TNm<+H#!<;_M89AC~dCXZgs3+amDU*bPyPDq~A#+xN|7 zV*+I+;xX1O^lv?BjDQp9?MqmBGTm6WcV`y0@D$R}@*Eh|ajZPGX-sbz{)0O*$yf4e zJqjZV-yCWzB*;AAlmPTm%$L5*Re!}Ky%gHA&`ioXDE zh%x8}r0*~!27Mk@L%;xgoRT!h<;)r7L-a+gFF?aK-0cXm%A=9bU>cb+4jH!=USl{f zX>@||8J6`2K`{#c{|Otwkrg%PJ1bEULi8y-Aw=c*CFQMU;vJEBGBPvBgL|2~ZSp6J zeKVeh&GY!jO-0(%#|f!IRj}>_G}ipS*P$-86gF^b?3!k%~SrFiQ{EE)9rADE4!)JU G{Qd)<{URO! literal 0 HcmV?d00001 diff --git a/test/api/fonts/SourceSansVariable-Roman.abc.ttf b/test/api/fonts/SourceSansVariable-Roman.abc.ttf deleted file mode 100644 index f2b141b48f41fdc00f46ccd83cd4d2d0b38c0909..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3168 zcma)8Z%kX)6+ibqV~jCijQ@ZE#{*-)jU8i*F@HjhLx`FZa12e?NNaJ-AG2WN8LuIk zG{sS*R+T12TR-GeH%(i_@?jsg51XPUqS};I(-d`Clx5X6MX7{36s0IjP*+WOzkA=q zBvG{HJ>7fHIrp63`E&2P_Yor^6FLn!4~-lg4Mn{cBK?<;?mrZpnACoM^EQ$0JQ33$ zntUz#Bg;7}QN;z!hmIT@j=%Jvy$}4y;0Grr_XUo>v3v=-tKg?6r-rAh^nY0go(BA% z5^pDRryc3vg8u}(JDE;osZoj%N%dTR4)gD9pVGmfi}Uk~r{^5ukCrjd{U4sk{P=~L ze-KrM!FQx)6El`yZ(ISr7y6+T1p0T%5DS|D?@FcfD>@$wz#f=nTwF>f=rej7{0ew| zI`(FrgKN=grkS{@0jMJhk$ z3hbZK`xu-gOIey`>oh3YFZ` zY|_*|*41IEuWYsk;{NSFMjXB-Psc9}or9gd`?&9E_+FQ8l9^hG%39p*t>vw?-InrD zNVqi%pP`
2hu%1kXsS|yF$A-P}o4!`CZnC=b6e9l7yZBt%P%7}mpZraxUf)+Lh$!9kt_GcGv$tK15-QDpnZ@jm2q`~BP)i98qh<#_E&1SXj>6?v4XTmM@*2XqbRWohzmC{szyN;&N z43)jE(h^nE1C^F3PXASD%@aChG{eSKxt9FwR|?H4$iVKYw4TgN#bK2clq6MdAiWe) z=_>L_5tXjq^~-9=Cw*I$8_6hLROwpsORFkvBAs+crR%6ex~tM=(#txPwvb0Q(J!zQ z1LUVJG$(mWTo}|a&Cn8^pjpt9befha4?2y}Nw45*OJX)hS0Tqo^?#VJ~VehO3^_{(TH?C)9VJ82Z?8O%Cq90+Mhoz8khvqay8 zR+46&4di>0hvpO)X<%&b^5i?|K+*uz7l z85td$B75?5ZjtQsx!IFsUrgjPWEZy);fCR{u#>a74Cy!}nuhpUC*f8yLBelcqdHE5 z^W&Q!3R46$zti|L#=GKu-s2lk9%bbAN@Ez67q_pF!pB*mi+%?;gs69M6(psP6*uWH z$XIa;JxAjACvVT9va`ZM^gJUy)*<**EnK1&E>R0tsTRUY?YJF&Fc3@Aaz3}jnU&Oh z9s~DA_^;r-RpBBi1~f(esuUTwB&;v5s*5DP#yIO~A)i=eI#?MY4Wn+;`10PRi$WX! zggg6yzJeV`AZ8GMW8m>`qR1pxE%pI;_Nb(16gvXE^j=A3;(LShva=*J^F79SvDf$? ziHgT{MLi`}4oYMrvgB0x8m$RoMVofc^os2Vt{=MRyuS&47@-K4^Oe9}EImz#erJuI zUoY+%Gxs0z<>ZvmCvvE}%E`r8xi{&2gw8Tl@;G?U*8R^nHa2dqUA=Z|Q|G9@x1~24 z8$#pbJcV;it1UFGuiFD2;MN(dbd0juBhC7)KU}(W>7(1D-^v#76DVRo0Ng!B?PJ9L zACt4O#l7*+ Date: Tue, 19 Mar 2019 00:41:41 -0700 Subject: [PATCH 065/101] add api tests for subset gvar & HVAR; bug fixes --- src/hb-ot-layout-common.hh | 3 +- src/hb-ot-var-gvar-table.hh | 24 +++--- src/hb-ot-var-hvar-table.hh | 19 +++- test/api/Makefile.am | 2 + .../fonts/SourceSansVariable-Roman.abc.ttf | Bin 0 -> 3240 bytes .../api/fonts/SourceSansVariable-Roman.ac.ttf | Bin 0 -> 3028 bytes test/api/test-subset-gvar.c | 81 ++++++++++++++++++ test/api/test-subset-hvar.c | 81 ++++++++++++++++++ 8 files changed, 194 insertions(+), 16 deletions(-) create mode 100644 test/api/fonts/SourceSansVariable-Roman.abc.ttf create mode 100644 test/api/fonts/SourceSansVariable-Roman.ac.ttf create mode 100644 test/api/test-subset-gvar.c create mode 100644 test/api/test-subset-hvar.c diff --git a/src/hb-ot-layout-common.hh b/src/hb-ot-layout-common.hh index a82e2d95d..fe4d78c41 100644 --- a/src/hb-ot-layout-common.hh +++ b/src/hb-ot-layout-common.hh @@ -1757,7 +1757,8 @@ 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 ())))) + unsigned int size = src->regionIndices.get_size () - HBUINT16::static_size/*regionIndices.len*/ + (row_size * remap.get_count ()); + if (unlikely (!c->allocate_size (size))) return_trace (false); memcpy (®ionIndices, &src->regionIndices, src->regionIndices.get_size ()); diff --git a/src/hb-ot-var-gvar-table.hh b/src/hb-ot-var-gvar-table.hh index 77454dcd9..50797bdcd 100644 --- a/src/hb-ot-var-gvar-table.hh +++ b/src/hb-ot-var-gvar-table.hh @@ -422,6 +422,18 @@ struct gvar HBUINT8 *subset_offsets = c->serializer->allocate_size ((long_offset? 4: 2) * (num_glyphs+1)); if (!subset_offsets) return_trace (false); + /* 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); + } + char *subset_data = c->serializer->allocate_size(subset_data_size); if (!subset_data) return_trace (false); out->dataZ.set (subset_data - (char *)out); @@ -446,18 +458,6 @@ struct gvar 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); } diff --git a/src/hb-ot-var-hvar-table.hh b/src/hb-ot-var-hvar-table.hh index 21d6b765b..e349e7a52 100644 --- a/src/hb-ot-var-hvar-table.hh +++ b/src/hb-ot-var-hvar-table.hh @@ -134,7 +134,14 @@ struct index_map_subset_plan_t /* Identity map */ if (&index_map == &Null(DeltaSetIndexMap)) + { + outer_remap.add (0); + hb_codepoint_t old_gid; + for (hb_codepoint_t gid = 0; gid < plan->num_output_glyphs (); gid++) + if (plan->old_gid_for_new_gid (gid, &old_gid)) + inner_remaps[0].add (old_gid); return; + } unsigned int last_val = (unsigned int)-1; hb_codepoint_t last_gid = (hb_codepoint_t)-1; @@ -189,6 +196,11 @@ struct index_map_subset_plan_t const hb_bimap_t &outer_remap, const hb_vector_t &inner_remaps) { + /* Leave output_map empty for an identity map */ + /* TODO: if retain_gids, convert identity to a customized map, or not subset varstore? */ + if (input_map == &Null(DeltaSetIndexMap)) + return; + for (unsigned int i = 0; i < max_inners.length; i++) { if (inner_remaps[i].get_count () == 0) continue; @@ -214,6 +226,7 @@ struct index_map_subset_plan_t unsigned int get_size () const { return (map_count? (DeltaSetIndexMap::min_size + get_width () * map_count): 0); } + bool is_identity () const { return get_output_map ().length == 0; } hb_array_t get_output_map () const { return output_map.as_array (); } protected: @@ -311,15 +324,15 @@ struct HVARVVAR const hb_array_t &im_plans) { TRACE_SUBSET (this); - if (!im_plans[ADV_INDEX].get_map_count ()) + if (im_plans[ADV_INDEX].is_identity ()) 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 ()) + if (im_plans[LSB_INDEX].is_identity ()) 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 ()) + if (im_plans[RSB_INDEX].is_identity ()) rsbMap.set (0); else if (unlikely (!rsbMap.serialize (c, this).serialize (c, im_plans[RSB_INDEX]))) return_trace (false); diff --git a/test/api/Makefile.am b/test/api/Makefile.am index 0f3dab73a..846b00ac4 100644 --- a/test/api/Makefile.am +++ b/test/api/Makefile.am @@ -50,6 +50,8 @@ TEST_PROGS = \ test-subset-vmtx \ test-subset-cff1 \ test-subset-cff2 \ + test-subset-gvar \ + test-subset-hvar \ test-unicode \ test-version \ $(NULL) diff --git a/test/api/fonts/SourceSansVariable-Roman.abc.ttf b/test/api/fonts/SourceSansVariable-Roman.abc.ttf new file mode 100644 index 0000000000000000000000000000000000000000..690d7d5392e21dc9ad01b86dfbe35ce8591c02ac GIT binary patch literal 3240 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 literal 0 HcmV?d00001 diff --git a/test/api/test-subset-gvar.c b/test/api/test-subset-gvar.c new file mode 100644 index 000000000..84cd48394 --- /dev/null +++ b/test/api/test-subset-gvar.c @@ -0,0 +1,81 @@ +/* + * 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 + */ + +#include "hb-test.h" +#include "hb-subset-test.h" + +/* Unit tests for gvar subsetting */ + +static void +test_subset_gvar_noop (void) +{ + hb_face_t *face_abc = hb_test_open_font_file("fonts/SourceSansVariable-Roman.abc.ttf"); + + hb_set_t *codepoints = hb_set_create (); + hb_face_t *face_abc_subset; + hb_set_add (codepoints, 'a'); + hb_set_add (codepoints, 'b'); + hb_set_add (codepoints, 'c'); + face_abc_subset = hb_subset_test_create_subset (face_abc, hb_subset_test_create_input (codepoints)); + hb_set_destroy (codepoints); + + hb_subset_test_check (face_abc, face_abc_subset, HB_TAG ('g','v','a','r')); + + hb_face_destroy (face_abc_subset); + hb_face_destroy (face_abc); +} + +static void +test_subset_gvar (void) +{ + hb_face_t *face_abc = hb_test_open_font_file ("fonts/SourceSansVariable-Roman.abc.ttf"); + hb_face_t *face_ac = hb_test_open_font_file ("fonts/SourceSansVariable-Roman.ac.ttf"); + + hb_set_t *codepoints = hb_set_create (); + hb_face_t *face_abc_subset; + hb_set_add (codepoints, 'a'); + hb_set_add (codepoints, 'c'); + face_abc_subset = hb_subset_test_create_subset (face_abc, hb_subset_test_create_input (codepoints)); + hb_set_destroy (codepoints); + + hb_subset_test_check (face_ac, face_abc_subset, HB_TAG ('g','v','a','r')); + + hb_face_destroy (face_abc_subset); + hb_face_destroy (face_abc); + hb_face_destroy (face_ac); +} + + +int +main (int argc, char **argv) +{ + hb_test_init (&argc, &argv); + + hb_test_add (test_subset_gvar_noop); + hb_test_add (test_subset_gvar); + + return hb_test_run (); +} diff --git a/test/api/test-subset-hvar.c b/test/api/test-subset-hvar.c new file mode 100644 index 000000000..0e60cc932 --- /dev/null +++ b/test/api/test-subset-hvar.c @@ -0,0 +1,81 @@ +/* + * 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 + */ + +#include "hb-test.h" +#include "hb-subset-test.h" + +/* Unit tests for HVAR subsetting */ + +static void +test_subset_HVAR_noop (void) +{ + hb_face_t *face_abc = hb_test_open_font_file("fonts/SourceSansVariable-Roman.abc.ttf"); + + hb_set_t *codepoints = hb_set_create (); + hb_face_t *face_abc_subset; + hb_set_add (codepoints, 'a'); + hb_set_add (codepoints, 'b'); + hb_set_add (codepoints, 'c'); + face_abc_subset = hb_subset_test_create_subset (face_abc, hb_subset_test_create_input (codepoints)); + hb_set_destroy (codepoints); + + hb_subset_test_check (face_abc, face_abc_subset, HB_TAG ('H','V','A','R')); + + hb_face_destroy (face_abc_subset); + hb_face_destroy (face_abc); +} + +static void +test_subset_HVAR (void) +{ + hb_face_t *face_abc = hb_test_open_font_file ("fonts/SourceSansVariable-Roman.abc.ttf"); + hb_face_t *face_ac = hb_test_open_font_file ("fonts/SourceSansVariable-Roman.ac.ttf"); + + hb_set_t *codepoints = hb_set_create (); + hb_face_t *face_abc_subset; + hb_set_add (codepoints, 'a'); + hb_set_add (codepoints, 'c'); + face_abc_subset = hb_subset_test_create_subset (face_abc, hb_subset_test_create_input (codepoints)); + hb_set_destroy (codepoints); + + hb_subset_test_check (face_ac, face_abc_subset, HB_TAG ('H','V','A','R')); + + hb_face_destroy (face_abc_subset); + hb_face_destroy (face_abc); + hb_face_destroy (face_ac); +} + + +int +main (int argc, char **argv) +{ + hb_test_init (&argc, &argv); + + hb_test_add (test_subset_HVAR_noop); + hb_test_add (test_subset_HVAR); + + return hb_test_run (); +} From 6dd1077b68e20e97b595c676267b964dd5cbbe1c Mon Sep 17 00:00:00 2001 From: Michiharu Ariza Date: Tue, 19 Mar 2019 01:00:53 -0700 Subject: [PATCH 066/101] fix test build --- test/api/Makefile.am | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/api/Makefile.am b/test/api/Makefile.am index 846b00ac4..c5f1f6d8b 100644 --- a/test/api/Makefile.am +++ b/test/api/Makefile.am @@ -66,6 +66,8 @@ test_subset_post_LDADD = $(LDADD) $(top_builddir)/src/libharfbuzz-subset.la test_subset_vmtx_LDADD = $(LDADD) $(top_builddir)/src/libharfbuzz-subset.la test_subset_cff1_LDADD = $(LDADD) $(top_builddir)/src/libharfbuzz-subset.la test_subset_cff2_LDADD = $(LDADD) $(top_builddir)/src/libharfbuzz-subset.la +test_subset_gvar_LDADD = $(LDADD) $(top_builddir)/src/libharfbuzz-subset.la +test_subset_hvar_LDADD = $(LDADD) $(top_builddir)/src/libharfbuzz-subset.la test_unicode_CPPFLAGS = \ $(AM_CPPFLAGS) \ From 0576253a340243eb8e4feabb3481f354a82a11a1 Mon Sep 17 00:00:00 2001 From: Michiharu Ariza Date: Tue, 19 Mar 2019 12:34:03 -0700 Subject: [PATCH 067/101] add gvar & HVAR retain-gids test cases --- ...SourceSansVariable-Roman.ac.retaingids.ttf | Bin 0 -> 3040 bytes test/api/test-subset-gvar.c | 22 +++++++++++++++++ test/api/test-subset-hvar.c | 23 ++++++++++++++++++ 3 files changed, 45 insertions(+) create mode 100644 test/api/fonts/SourceSansVariable-Roman.ac.retaingids.ttf diff --git a/test/api/fonts/SourceSansVariable-Roman.ac.retaingids.ttf b/test/api/fonts/SourceSansVariable-Roman.ac.retaingids.ttf new file mode 100644 index 0000000000000000000000000000000000000000..a77d9da93a1e9df9be50cf0e68757564b059153c GIT binary patch literal 3040 zcma)8e{5S<75?sfj^jFR;y8(&xQ%O4$4Ntzrf%XUOV&AV`eRy1vLuwP60B~1IW%^H zW7Z$=M>8QdO&}_mQ2&AnAry)P2qZw$T17&tCWNL@Dp3$Z)sUtVRc$odGA0!Bo%^1X zwhFB8>E3(3bI|cYcQW(|4M>iO&=F4vp@K9e;7*M}YBj;uE7| zePdhQfBb;+ZxjDM^>QkAHkA1d;J!s19nYlZ@pZEYFoi!q#rap)PP;h&n)px8o}K!Z z=TqkZ)l2-BY0fuHDbAIN{d?)j)I`lMSKea0e&TqV3iqoe?+|~3IFiof&p4}bjrgm? z-r2eF6n=sO#9hShOzO-$wkv~J>J4X8naS@3uN-7PU*iJB=I0jjg+Cz1^!@W6TSEY zRoM~SHxlqxbo4iMC4xbR(fDH9?veJHuf+R2+qzqaM}18Ldz*c=W%d5pNc-CF5}~&3 zEzLjm?CRaMe-D=camN0kj*Sv4a-8u3r8>Hkf&BXzGvIV8&(o={-c)xiSzBHERCM3S z-qw-*yZY;Vq31k3^Fza5>S+x413S7WlgWv$hFbr&Mph@!g##DhK^CPNQDI*Wm|8l( zwaZWvTUD~-^FRCUuU@L?y${p8CG&s!==myptY7P0yXl%Ymx!HQH+>oNIH5j(>?PC{ z#D&Mav38fw+eO;R=?$}rUTWnTTwc!NV&*Cn1(UxZEM0U}|FaCB?zklkh-4(bhIEEI_K zSa`GW=6YNYwa~UQO4TX53E#NgBvv&ZNEF8w%2(MeS<&~ZEgLAu9a}arg8$gE_Us$r*0*`v%mffgUHjgSphhf-y58OuFmbaqCNZ4}uMqE{aHsg!7-V3jB z(UvRGZY^J=WGu1x=-o33C=VlO*v2fQ`p1%w7%Q8>P5CT?U~`+F6^eqY0S_* zO=*Pu3v4;O%wE_IlReAXFb2txp*9?@Wj1s8GOflj8LmUy!z8ZN|4zUF)VkPwxHXmXGeuD6`Hm$CF%-Sw0D$l;z|0$tUzH+9mZfG0q+M zq(~c<)(ORfqVTSKk2bj!-!)v1kd1%4j zsyl{`N9vB~x+A*o$a>x4Q@q4a&hdvY(f!X=9*zaY*79?)itP*e+?=S+q^I+GW^;d8 zOU`Vyc1}DcuB?0n#_8#Fp65Mg@~K(nqMeu8F1~Xo`1ZYqi`p8>&2K1l`#d6q4i5hH`n5mbZLju;zV99%z zE?v6(mT!B#-|r6?Hy6uSSO1B0`uVPRum7VgI*?ve{JYjM4l>3Luk$`*{M#4a)(~C! z(AV;G>fE_=(}{@p{+&?3h2Y|1&~$k!s>rq@boKJm($a5NJ Date: Tue, 19 Mar 2019 16:00:01 -0700 Subject: [PATCH 068/101] add api test cases for HVAR with index map (and fix) --- src/hb-ot-var-hvar-table.hh | 2 +- test/api/test-subset-hvar.c | 76 ++++++++++++++++++++++++++++++++++--- 2 files changed, 71 insertions(+), 7 deletions(-) diff --git a/src/hb-ot-var-hvar-table.hh b/src/hb-ot-var-hvar-table.hh index e349e7a52..77d96b7cf 100644 --- a/src/hb-ot-var-hvar-table.hh +++ b/src/hb-ot-var-hvar-table.hh @@ -170,7 +170,7 @@ struct index_map_subset_plan_t last_gid = gid; } - map_count = last_gid + 1; + map_count = last_gid; for (gid = 0; gid < map_count; gid++) { hb_codepoint_t old_gid; diff --git a/test/api/test-subset-hvar.c b/test/api/test-subset-hvar.c index 0cc0a17e7..3326e6db1 100644 --- a/test/api/test-subset-hvar.c +++ b/test/api/test-subset-hvar.c @@ -30,7 +30,68 @@ /* Unit tests for HVAR subsetting */ static void -test_subset_HVAR_noop (void) +test_subset_map_HVAR_noop (void) +{ + hb_face_t *face_abc = hb_test_open_font_file("fonts/AdobeVFPrototype.abc.otf"); + + hb_set_t *codepoints = hb_set_create (); + hb_face_t *face_abc_subset; + hb_set_add (codepoints, 'a'); + hb_set_add (codepoints, 'b'); + hb_set_add (codepoints, 'c'); + face_abc_subset = hb_subset_test_create_subset (face_abc, hb_subset_test_create_input (codepoints)); + hb_set_destroy (codepoints); + + hb_subset_test_check (face_abc, face_abc_subset, HB_TAG ('H','V','A','R')); + + hb_face_destroy (face_abc_subset); + hb_face_destroy (face_abc); +} + +static void +test_subset_map_HVAR (void) +{ + hb_face_t *face_abc = hb_test_open_font_file ("fonts/AdobeVFPrototype.abc.otf"); + hb_face_t *face_ac = hb_test_open_font_file ("fonts/AdobeVFPrototype.ac.otf"); + + hb_set_t *codepoints = hb_set_create (); + hb_face_t *face_abc_subset; + hb_set_add (codepoints, 'a'); + hb_set_add (codepoints, 'c'); + face_abc_subset = hb_subset_test_create_subset (face_abc, hb_subset_test_create_input (codepoints)); + hb_set_destroy (codepoints); + + hb_subset_test_check (face_ac, face_abc_subset, HB_TAG ('H','V','A','R')); + + hb_face_destroy (face_abc_subset); + hb_face_destroy (face_abc); + hb_face_destroy (face_ac); +} + +static void +test_subset_map_HVAR_retaingids (void) +{ + hb_face_t *face_abc = hb_test_open_font_file ("fonts/AdobeVFPrototype.abc.otf"); + hb_face_t *face_ac = hb_test_open_font_file ("fonts/AdobeVFPrototype.ac.retaingids.otf"); + + hb_set_t *codepoints = hb_set_create (); + hb_face_t *face_abc_subset; + hb_set_add (codepoints, 'a'); + hb_set_add (codepoints, 'c'); + hb_subset_input_t *input = hb_subset_test_create_input (codepoints); + hb_subset_input_set_retain_gids (input, true); + face_abc_subset = hb_subset_test_create_subset (face_abc, input); + hb_set_destroy (codepoints); + + hb_subset_test_check (face_ac, face_abc_subset, HB_TAG ('H','V','A','R')); + + hb_face_destroy (face_abc_subset); + hb_face_destroy (face_abc); + hb_face_destroy (face_ac); +} + +static void +test_subset_identity_HVAR_noop (void) { hb_face_t *face_abc = hb_test_open_font_file("fonts/SourceSansVariable-Roman.abc.ttf"); @@ -49,7 +110,7 @@ test_subset_HVAR_noop (void) } static void -test_subset_HVAR (void) +test_subset_identity_HVAR (void) { hb_face_t *face_abc = hb_test_open_font_file ("fonts/SourceSansVariable-Roman.abc.ttf"); hb_face_t *face_ac = hb_test_open_font_file ("fonts/SourceSansVariable-Roman.ac.ttf"); @@ -69,7 +130,7 @@ test_subset_HVAR (void) } static void -test_subset_HVAR_retaingids (void) +test_subset_identity_HVAR_retaingids (void) { hb_face_t *face_abc = hb_test_open_font_file ("fonts/SourceSansVariable-Roman.abc.ttf"); hb_face_t *face_ac = hb_test_open_font_file ("fonts/SourceSansVariable-Roman.ac.retaingids.ttf"); @@ -96,9 +157,12 @@ main (int argc, char **argv) { hb_test_init (&argc, &argv); - hb_test_add (test_subset_HVAR_noop); - hb_test_add (test_subset_HVAR); - hb_test_add (test_subset_HVAR_retaingids); + hb_test_add (test_subset_map_HVAR_noop); + hb_test_add (test_subset_map_HVAR); + hb_test_add (test_subset_map_HVAR_retaingids); + hb_test_add (test_subset_identity_HVAR_noop); + hb_test_add (test_subset_identity_HVAR); + hb_test_add (test_subset_identity_HVAR_retaingids); return hb_test_run (); } From 92bc74055831acae3d296f6e1470fa4cd4d193fd Mon Sep 17 00:00:00 2001 From: Michiharu Ariza Date: Tue, 19 Mar 2019 18:23:14 -0700 Subject: [PATCH 069/101] update CFF2 test results --- test/api/fonts/AdobeVFPrototype.abc.otf | Bin 7456 -> 6772 bytes .../api/fonts/AdobeVFPrototype.ac.nohints.otf | Bin 6780 -> 6096 bytes .../AdobeVFPrototype.ac.nosubrs.nohints.otf | Bin 6844 -> 6160 bytes .../api/fonts/AdobeVFPrototype.ac.nosubrs.otf | Bin 7060 -> 6376 bytes test/api/fonts/AdobeVFPrototype.ac.otf | Bin 6996 -> 6312 bytes .../fonts/AdobeVFPrototype.ac.retaingids.otf | Bin 7000 -> 6316 bytes 6 files changed, 0 insertions(+), 0 deletions(-) diff --git a/test/api/fonts/AdobeVFPrototype.abc.otf b/test/api/fonts/AdobeVFPrototype.abc.otf index cc477088ebdec87d1970e7d4acc8ce50dd7cd36c..2cd6965c78e5a119ad1cb843c84539c4a67c1847 100644 GIT binary patch delta 367 zcmWm7-z!657zgn0dv?rO&d$ySvV_Gh#4;_pkg{E<-N>?IR(7$|MrUzmu~;dVB$w0c zN-nriE9FQ20kq_nTek~orH~8JJm;&Qp2&()ZO0AH{h-U zC{2)0|H=kAN#2@_`UA6Pg+qWv$jPV{((XH}`#@Ws{HCw!%iX4TojgkJO__QQ?d%jJv{XhEpvgB=Y*_hMQ>VYI91=Of9Y*42$CY;j(FWGCD`GRzeTMA5*RZ}nIAX>Xw zm>zppH|Ks{im&7gz?sM`t>o(u2+#s+-UDWhqOuU2$#BUr$QNB6J3s*<|BzExC$5B( zXOuW+9bNodDPPuFBtEY6s6r7D_KN{w3~u&}ey%w``I@}ZWCa4vba#2sG3^&Az5xQ6 a!3GgK^$M#)!;>{;oq81t6$a5JF#H38gjfmy delta 1061 zcmXZb-%C?r7zgn0nVW99xoK(}nogTK{EtYKR0Y?pxRNu<{`R-^GaK-`LI?+*8eeT83Bj&5jyF0>7;feg|2Y@Tg{AXk^ za;+#9xB;X$G7t8|B5~vz3(VOE&Rpj6_Z4M*%wIDfjrK+^c^=xGI1T#_3x}etq;y(d z0&e|6AQ~GOvOmEa=69KUVv(UZtZfY490Zd4BC+1>+2J3|pK=iA<5&6z)H_rI`}i3C zu?kPNJ;lKI%{$x2?{9|eEM{?LHRhu4RALYYY_PCqAPH8CsBuFf2NSG>FiAV`iH5~~ z(TC5h_%Q{jIecMk#aG&jZ%}F;-&tSNVlQSWFw5Uzu@oFA+t1Nt{7SItN4@ZBRg0jQ zjd{j?u}sXsA69xq54e~p5NpMDu>ni0*I}8xtz(68iRggdD8J~y8siMCQ!nNB^Cc+~ z>%}h7zy=#-i(%1^O;$?93Na)e74xyhr-fpJmTR-kijOK3Nn9+JY1Ib)g5S648(ja@ zCm;Z-R^sjLLG zY8g&CjdZ$%J@kVm#pL44bm>Q`VN4)$H<(nC(WldEL8g z9`hEOPWM^!w)>eGFalo7FxreA9I!ONXUuTG;J=Df8hd42aHZ+l@=|Y6ZC-U}{R1)! B-6jA4 diff --git a/test/api/fonts/AdobeVFPrototype.ac.nohints.otf b/test/api/fonts/AdobeVFPrototype.ac.nohints.otf index 935bdbfb13d9aed807bcf5f27eab0863051d5e0b..fa593f594a1c43ece1978741563e5c54d15170a9 100644 GIT binary patch delta 276 zcmexkazTHBLjAFc2fi{eur)9+F!cL|IR>r%yCQ>ufn5N|Z}Jb;H;P_YF$c(>0^}zI zhd73ukb44Tvws2dUnQ0$7AfVbR5LJeJOJ`Fl5-Oa7}}UFFfed(0Qos-K>15LT6sWz z36Q@cBQ-HaU={}t&^`|!-z*~|b>e(>CoUNvM0tmjjT`lbcvkz`z^; zWODfc3AVh%+|*ys8(skUAq)(>F$MX>CI2rn7%?!2Z`k-EgOz1{^}ku z*h2MK7#JB?8JK{285x)vSQy&=A7T8@5W;X9C;|rmpD^$N89-TPCWg(sIS&c|0DWmk APyhe` delta 965 zcmXBTO-NKx90l+*pEf$?XjxjOFB^6615HE&4YidZVl`8n1RKqmMPrg8LW_|Wu3Qwh zilCN85JWB8LMq{n9slwUY%=ekU$A{zBij(2%K*l(8 zq&FEKz?gfQ`6=_2%RK+Kvb-M%I?R)azWAlkojpO$*T!6*Nc7#hmv$;R#Sl&;herH8 zc)-CwnS;sr$N)U2ZTW}z`{T*J?YYr;Ad@k(YvAhOka>ez;854&`Gn8=>P1WEM9g3B z_S8*``_H9@;erot4tBu{0A>N7SbOoAcHj$) zS;SZN*R42+Zxop0_k=71{~O0IP(Oa8IP^WZ6tiYUMCM|VwIj=AHh!_wD??afT_hXi z8QF|w_8YOnt8HMFb*T(sjkO~KSZAG$4Vp(ef1yILY?9s5g-s61l`-kS?s1u{lr{3C zEW{Si9+k~j?#(tk`P86Tb%`vunoax#=l9tI@78~JScgH)Dt5IF%8jx*>TL01K$a|% z6|!2kOAg@2B{O75?w3U}EK?DC;O5&WAXApeQrTuTzxia?Ev3j&J;OU_I}`=7O6G7m zwqvc3RkB7N=0faENuv!iy&D0BJb7IDkj_rTYF5Cvz|3MVUB*884t5R4_<#Z}{2j~` cH`rdGfVSZXJF70AadRqfd(!(PEA4scA1t28*Z=?k diff --git a/test/api/fonts/AdobeVFPrototype.ac.nosubrs.nohints.otf b/test/api/fonts/AdobeVFPrototype.ac.nosubrs.nohints.otf index 85f6cf6a34e446d02ee0cc2fd3e1cc9d428d1478..85e921015ede7fed0be196ba63bcea1a79a85066 100644 GIT binary patch delta 276 zcmdmEI>BIqLjAFc2fi{eupMAvVCeS^a|~MjcSQyR1A73F-{c>xZxp?*Vh)gh1;|eb z4si@QA@^ho0|SQ%kpC*NEU`!_SEZVPfl~p<*GSGyEMRD3x&Y*R0Qos-K>15LT6sYJ z79f8`MrvY;z$^|PpnV-czF9^_>csi%PFyiSj!H&uNd;RH!%7AQt_C2VCpWR8fPoq0 z4z3;`!IqbpoBHc{!wVoE=m6fBg8br={}&mI7#PGqY<#hTl_hWa?&ir;*vyz1IwqfC z3)N#`U}RurU;^r8WMF1sVQBk*gz-N^2*Yil2pIf-!oUY)0A-n(7&iarJSYGFw!TS5 delta 965 zcmXZbO-NKx7zW^Xes$ET(XzBmCmVI}2PY{NwA5CDh?P^D0v*kmMPrg8LW_|IX=&&p zLTGCQL9}gA(87g5MYJjG2TQHY{7*|x?|Et9KKJ{+d+t4RzI*Oy`cZ1-(LVX+S)6tE<7BX0Mdq;ceY2HqEq>^ExZU zac{NLb!=?fdoDQ$2Rv}{UI$!oW5hgn80^CoD-pb)?RZC{vR~%nJu5-X0A>Ln7`yR_ zw&F94S;QCC*R9x(uN0W$Q7cQp|HknP)Qj&)4tvr;0< zWkjBk`Pkys!?MB3-fXjyOAQJY7s*nq*~A}kevduD|6jYKLZD_9J6Z>2qpU$yw)ik$ zw=9umvQoB44&cur(`7*Jl?5^+lVN*e^X(4s%OY7Uo2}*-cZO{#MV8_jzCl}|$deT^ zi_5VUYniN&5qXdcu`eZs*2uID_?YC#qtb&kR>D@Z0)7Q%7Q5&&_Ru%5YdFFU3N-RQ em?<{cJ|T}b;}9#W4v%qiDtAl5{kSdVS>PXoXvxn2 diff --git a/test/api/fonts/AdobeVFPrototype.ac.nosubrs.otf b/test/api/fonts/AdobeVFPrototype.ac.nosubrs.otf index ad4d53b874827c32af3b4a1a109d8f88b68b148b..76f264ec41cf58396250dce530f5ae759b152c86 100644 GIT binary patch delta 280 zcmbPY{=#sALjAFc2fi{eu+Ly%VCeS^a|~MjcSQyR1BV8X-{c>xZxp?*Vh)hM2FOnc z4si@QA@^ho0|O@ykpC*NEU`!_SEU-r{{iG{Be(>CvFoUM0tcLb2nlbcvkz`z^; zWOAnf3AVh%+|*ys8(skUISdTEF$MX>CI2rn7%?zNoY?rHfsN($s_!o+=dhcxF?0ac zGfaNN6{yF;z{tSLzy#znGB7i+Ftq(Y!uX#dgyA+w1ql3q!oUZlfU?X?44W@=9TWfn Do*79_ delta 969 zcmXZbOGs2v90u_3Ixk;iqh%?YO*T4%4}1_RXsE3Okurw}3UoAM7LG}ZNG(QMxGr=N z5wvMx1VOZE8?aH9Au?KgIln`D<)6cBMEGx&~z4 zV~+GCVndindCB~odEgSSzpf}71pE!m)A9b;#lY>HokGiYG?K zyuG*&WVx9$6S1)&xK6Xo3J~vLEYZKaFuur~#|bYCT^=4WuTTpVj67N#_qv_6tA8g4 ze05&eagN7iOKN9w6b?K{VRYbv8x!W4V{i~N>{R0g?ZR6cmBTV0@7M`q4lql2&)SWT zv;&`D%rZW+ziGuGe4)Ss-wiSh{yvUhqF#JUa_Ad;ryEv8WGWJo3>_Jf;mPk>*R$Wqy6H9vVW?3PmGsGj2!v>kg6 z3uUFu;qBOtwOm%pYFWS=u@A*Z>t%X3(irk&jr1U$oru+}f&T1{d89MaEu2O hXyNx@X1Kw2hCr%yCMU~e*xq-`3LJ8MX#%v!@$6i1LP+J zhd73ukb5!($Ug(*ze+4iEK60 delta 968 zcmXZb%S%*I90&04I4@sQqh%_Z-E7ps2R;xDG}KmxNSQ+f1vWln7G0AR8E7%l!c~i2 zs|aeB5d_g+5V&v?R7eDcQCMna<~uDpeb1K$?&to#zkBAKx#!-Qte7-iR<$fS| z7Vx%qpXoULWOp(FWZngE{bX-#!?~x)`#@F!^M&46M{K%ej@Po1%-`bU@hhcn0{qGBFe%4Bt!7=6ViuQz9{R>p}2%=pjQS;f^H( z{df$7rkJzccya{ZQ!GQA5P{*iJG3)@ZGrh6kMkp!N5{--Gyp}7&lcJPzI^x5i96N7 zrhvDR<8eE^Cp8Wa{77T+zzZKH%#_FA0H)cg!%NzW_cSI)Wg$MW<6ssri}=XehflN{ zpJB`rzOcVxMLxb#V4lZOSpoh$j$fn!d{1%cTl}EwRzzhUmRLKoQs&?%I|DL|W!8se zv+R*=SYf{ftDJ2UYply<2G&_SG6Nf|bFfJZDAzAhD3z_UPkOM$L3uJJ9oT17A**Gb zJSI!9&8vrHo0WUB!%iVJC{Zr5L$$-7G zLRQII*(EuEACJtEVYy!(k`bAT+7mb5z5yXwCd*}~)%@a{VYiebSM@BPpzTl;%Nm)> z+p!&Mm8_9_! zKqj{akYLM8%uW6EyaDJiZUY7e-k5^?;*$Rt8H^YhBziW!aA0F`GP%+@S%=+>jiCdm zo?-GDu0TB&21W)}1|}evk%5_kg`w^L5yt-vAq=-cDnQ`>69zsY#mKB{mU=HJW{@xjKG9Bm zhA~U{!v2O82l15x^E?`58Tj)!ev$g|J;|YO@q?~g5s|rAV(rLsnT?<9^ve*ISs#`S zvP-sLh5aV1a<)yZu`ZSASZD3XbZoHB#wN|9T)#-6ST@UE>A@BU<;tjZV4qQ$tduqK zxGcgpuO5*tR_@IXJNeY0Sapdkx0)^d0oV806N3N!%Q_5d*08Jlpxh{{qs}%b1NO=? zSs|-shvWc$JTg;;=L4@o{{WId$Y=ln From 325918172e7a346b6de6a8afcd94a4d7fd35d5c0 Mon Sep 17 00:00:00 2001 From: Michiharu Ariza Date: Wed, 20 Mar 2019 15:10:59 -0700 Subject: [PATCH 070/101] added TT VF full font test & fixed bugs updated CFF2 VF api test results too --- src/hb-ot-layout-common.hh | 53 ++++++++++++++---- src/hb-ot-var-hvar-table.hh | 51 +++++++++-------- test/api/fonts/AdobeVFPrototype.abc.otf | Bin 6772 -> 6952 bytes .../api/fonts/AdobeVFPrototype.ac.nohints.otf | Bin 6096 -> 6272 bytes .../AdobeVFPrototype.ac.nosubrs.nohints.otf | Bin 6160 -> 6152 bytes .../api/fonts/AdobeVFPrototype.ac.nosubrs.otf | Bin 6376 -> 6336 bytes test/api/fonts/AdobeVFPrototype.ac.otf | Bin 6312 -> 6460 bytes .../fonts/AdobeVFPrototype.ac.retaingids.otf | Bin 6316 -> 6464 bytes ...iable-Roman.default.1FC,21,41,20,62,63.ttf | Bin 0 -> 5464 bytes ...ceSerifVariable-Roman.default.61,62,63.ttf | Bin 0 -> 5432 bytes ...fVariable-Roman.default.D7,D8,D9,DA,DE.ttf | Bin 0 -> 5924 bytes ...le-Roman.drop-hints.1FC,21,41,20,62,63.ttf | Bin 0 -> 5464 bytes ...erifVariable-Roman.drop-hints.61,62,63.ttf | Bin 0 -> 5432 bytes ...riable-Roman.drop-hints.D7,D8,D9,DA,DE.ttf | Bin 0 -> 5928 bytes .../data/fonts/SourceSerifVariable-Roman.ttf | Bin 0 -> 586100 bytes test/subset/data/tests/full-font.tests | 1 + 16 files changed, 72 insertions(+), 33 deletions(-) create mode 100644 test/subset/data/expected/full-font/SourceSerifVariable-Roman.default.1FC,21,41,20,62,63.ttf create mode 100644 test/subset/data/expected/full-font/SourceSerifVariable-Roman.default.61,62,63.ttf create mode 100644 test/subset/data/expected/full-font/SourceSerifVariable-Roman.default.D7,D8,D9,DA,DE.ttf create mode 100644 test/subset/data/expected/full-font/SourceSerifVariable-Roman.drop-hints.1FC,21,41,20,62,63.ttf create mode 100644 test/subset/data/expected/full-font/SourceSerifVariable-Roman.drop-hints.61,62,63.ttf create mode 100644 test/subset/data/expected/full-font/SourceSerifVariable-Roman.drop-hints.D7,D8,D9,DA,DE.ttf create mode 100644 test/subset/data/fonts/SourceSerifVariable-Roman.ttf diff --git a/src/hb-ot-layout-common.hh b/src/hb-ot-layout-common.hh index fe4d78c41..02c422a86 100644 --- a/src/hb-ot-layout-common.hh +++ b/src/hb-ot-layout-common.hh @@ -1754,20 +1754,35 @@ struct VarData 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 (); - unsigned int size = src->regionIndices.get_size () - HBUINT16::static_size/*regionIndices.len*/ + (row_size * remap.get_count ()); + /* Optimize short count */ + unsigned int short_count = src->shortCount; + for (; short_count > 0; short_count--) + for (unsigned int i = 0; i < remap.get_count (); i++) + { + unsigned int old = remap.to_old (i); + if (unlikely (old >= src->itemCount)) return_trace (false); + int16_t hi = get_item_delta (old, short_count - 1) & 0xFF00; + if (hi != 0 && hi != 0xFF00) goto found_short; + } + +found_short: + shortCount.set (short_count); + regionIndices.len.set (src->regionIndices.len); + + unsigned int size = src->regionIndices.get_size () - HBUINT16::static_size/*regionIndices.len*/ + (get_row_size () * itemCount); if (unlikely (!c->allocate_size (size))) 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 () + (row_size * remap.to_old (i)), row_size); - p += row_size; - } + memcpy (®ionIndices[0], &src->regionIndices[0], src->regionIndices.get_size ()-HBUINT16::static_size); + + for (unsigned int i = 0; i < itemCount; i++) + for (unsigned int r = 0; r < regionIndices.len; r++) + { + hb_codepoint_t old = remap.to_old (i); + if (unlikely (old >= src->itemCount)) return_trace (false); + set_item_delta (i, r, src->get_item_delta (old, r)); + } return_trace (true); } @@ -1782,6 +1797,24 @@ struct VarData HBUINT8 *get_delta_bytes () { return &StructAfter (regionIndices); } + int16_t get_item_delta (unsigned int item, unsigned int region) const + { + const HBINT8 *p = (const HBINT8 *)get_delta_bytes () + item * get_row_size (); + if (region < shortCount) + return ((const HBINT16 *)p)[region]; + else + return (p + HBINT16::static_size * shortCount)[region - shortCount]; + } + + void set_item_delta (unsigned int item, unsigned int region, int16_t delta) + { + HBINT8 *p = (HBINT8 *)get_delta_bytes () + item * get_row_size (); + if (region < shortCount) + ((HBINT16 *)p)[region].set (delta); + else + (p + HBINT16::static_size * shortCount)[region - shortCount].set (delta); + } + protected: HBUINT16 itemCount; HBUINT16 shortCount; diff --git a/src/hb-ot-var-hvar-table.hh b/src/hb-ot-var-hvar-table.hh index 77d96b7cf..19257f6d0 100644 --- a/src/hb-ot-var-hvar-table.hh +++ b/src/hb-ot-var-hvar-table.hh @@ -121,7 +121,16 @@ struct DeltaSetIndexMap struct index_map_subset_plan_t { + enum index_map_index_t { + ADV_INDEX, + LSB_INDEX, + RSB_INDEX, + TSB_INDEX, + VORG_INDEX + }; + void init (const DeltaSetIndexMap &index_map, + unsigned int im_index, hb_bimap_t &outer_remap, hb_vector_t &inner_remaps, const hb_subset_plan_t *plan) @@ -132,14 +141,18 @@ struct index_map_subset_plan_t max_inners.init (); output_map.init (); - /* Identity map */ if (&index_map == &Null(DeltaSetIndexMap)) { - outer_remap.add (0); - hb_codepoint_t old_gid; - for (hb_codepoint_t gid = 0; gid < plan->num_output_glyphs (); gid++) - if (plan->old_gid_for_new_gid (gid, &old_gid)) - inner_remaps[0].add (old_gid); + /* Advance width index map is required. If its offset is missing, + * treat it as an indentity map. */ + if (im_index == ADV_INDEX) + { + outer_remap.add (0); + hb_codepoint_t old_gid; + for (hb_codepoint_t gid = 0; gid < plan->num_output_glyphs (); gid++) + if (plan->old_gid_for_new_gid (gid, &old_gid)) + inner_remaps[0].add (old_gid); + } return; } @@ -257,7 +270,7 @@ 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], outer_remap, inner_remaps, plan); + index_map_plans[i].init (*index_maps[i], i, outer_remap, inner_remaps, plan); outer_remap.reorder (); for (unsigned int i = 0; i < inner_remaps.length; i++) @@ -294,14 +307,6 @@ 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); @@ -324,17 +329,17 @@ struct HVARVVAR const hb_array_t &im_plans) { TRACE_SUBSET (this); - if (im_plans[ADV_INDEX].is_identity ()) + if (im_plans[index_map_subset_plan_t::ADV_INDEX].is_identity ()) advMap.set (0); - else if (unlikely (!advMap.serialize (c, this).serialize (c, im_plans[ADV_INDEX]))) + else if (unlikely (!advMap.serialize (c, this).serialize (c, im_plans[index_map_subset_plan_t::ADV_INDEX]))) return_trace (false); - if (im_plans[LSB_INDEX].is_identity ()) + if (im_plans[index_map_subset_plan_t::LSB_INDEX].is_identity ()) lsbMap.set (0); - else if (unlikely (!lsbMap.serialize (c, this).serialize (c, im_plans[LSB_INDEX]))) + else if (unlikely (!lsbMap.serialize (c, this).serialize (c, im_plans[index_map_subset_plan_t::LSB_INDEX]))) return_trace (false); - if (im_plans[RSB_INDEX].is_identity ()) + if (im_plans[index_map_subset_plan_t::RSB_INDEX].is_identity ()) rsbMap.set (0); - else if (unlikely (!rsbMap.serialize (c, this).serialize (c, im_plans[RSB_INDEX]))) + else if (unlikely (!rsbMap.serialize (c, this).serialize (c, im_plans[index_map_subset_plan_t::RSB_INDEX]))) return_trace (false); return_trace (true); @@ -424,9 +429,9 @@ struct VVAR : HVARVVAR { TRACE_SUBSET (this); if (unlikely (!HVARVVAR::serialize_index_maps (c, im_plans))) return_trace (false); - if (!im_plans[VORG_INDEX].get_map_count ()) + if (!im_plans[index_map_subset_plan_t::VORG_INDEX].get_map_count ()) vorgMap.set (0); - else if (unlikely (!vorgMap.serialize (c, this).serialize (c, im_plans[VORG_INDEX]))) + else if (unlikely (!vorgMap.serialize (c, this).serialize (c, im_plans[index_map_subset_plan_t::VORG_INDEX]))) return_trace (false); return_trace (true); diff --git a/test/api/fonts/AdobeVFPrototype.abc.otf b/test/api/fonts/AdobeVFPrototype.abc.otf index 2cd6965c78e5a119ad1cb843c84539c4a67c1847..022e6fae5057de770500b8dc77580db42c578035 100644 GIT binary patch delta 1908 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 delta 1451 zcmX|BeQZ-z6uI} z_2ZrQ`uh61wd>e`ZEPd358^*+Bq|UgCL$O~Oo)jlM2+zu6Ul~@%=1R#P436<+;h)8 z=XcJ%;b(4a6&o5G8`z^P%xvtyOFMVEhtKzIXN>*Dm}B3ry|r(Aqknamv63*!w0gql z9!z=}a|(nb4STk_-Ih*tFN=< z19q11i-ec7dOD9V+iSFhIvPq2ds+|v`+N8g!i|hA-G8L*sBhsWb2GMjmiD=oIoM{y$!E)wKVF-fxOz2ZCiX zA*MK_h#g%hosj}YM%)Zdk-C5HRCQ=QhU^78ns*%1XKzH^-2(>p>miS z&tDkD@GQlbb3s4(IKm-{72rRu^+n>DWVBa}tAZmG4o8m1FqV!cC0R|#vZly#I;|vS zEvqEc!dhSV;HMbLafln0pht-BDQ_~Z31xF$y>Oz}Pz2}Pil)z9)wTOy=*0GZZTC73IR@xtI_7K4-vX_P}x zp%Ox0QVfv_IP+_B3r0UC#(f+Hnh`VUu$0#XM`8ACY6OR4#Cn!PU$4l4yns;^k^KK| z;^<}^AnW-cd6gi0d9#Ab?srB~E!nQkz}L@B%WprRDB|l0=C@j?rPU-~yupZiQ+uxiL<{aW0rC@qLmWd+$UT|Dz`&sceISz>+BB^|9ipuifSz>192#1uZi5_h159YDTW22g-K zjA611p2DERfYD2qI-9WO;y86^{y#WMGJyoWt(BIh@gwRVM7q zOPhFpo39Ma%pi>nSNA+S52p8eI!#`~CeK(u`3zgA3NuiQm4OLJG6KVoMSEZ$WI6kaSS;j_hbqK1BVHa|0=O8u}CRbrJ8|( zQvt}=NX|_xU}$5y0OWfB`8jE2iS70OQv_yl@Bl6B0P@W;fCB7c zOzRjJxMF~Om5khy3brJMl|X(2kk6BwSW&>h3~~!s50GHXOUzCE^}OK)kPmbOZ%jdc zamoLS3`PtL;vazW3QP>F48pQNRuzv5i0Tq#15q*(n^_opSY`5-?{1FgxB1Gz%nUSw zf#K?&XXiom4UW6)lh?4xGj>cq!xpN;!oUbLhY2Xb2n;nAhPMAl82>YbFx&>I1%dxh PfFTN&W!~({epmniRX11# diff --git a/test/api/fonts/AdobeVFPrototype.ac.nosubrs.otf b/test/api/fonts/AdobeVFPrototype.ac.nosubrs.otf index 76f264ec41cf58396250dce530f5ae759b152c86..8d268686ecaad8d86e572ac38a4d6ccd98d03b26 100644 GIT binary patch delta 424 zcmXwyze_?<6vw~sU4N!!zXpd4355uubZBbO3a!DceAtkbuQVKbWhL^cL8)+vw%1V6 z&{z~Mt_492ZDr73kd;J^K@?)=2_3lK`~7^+x%X7uH|}gm5JG4{4h*pozuzMq&$$46 z0y<$V;+uZ4zg7XgOj$lid{H|~0tP~Chr<1y*@ApRyh|Jl3qIk#yL>=gCjJmJ;(Cwd z*aD1?#J!7>m_nW>Z~P`+Udo8s^(R-~3eXg%;4GOC<1G{E04?+q4<;$lNB9C@G7~$J zQd-t6;GB4X*er>13T%?BX^2F(B1(y$_gsZ|6ljj7RyWf15l1aIW|i#|Gw^CkrM?3?>10)-cnwnyMn>D1;Am1~M@?-}IsZcrMW^Am F^$!OvT#5hy delta 460 zcmW-eOGpAy6o&tqtB#eGJr*u1BwSPkkyNWT5=fgea(X~QPA^8vSC`xQKym+Hb zZ7E75mWVFL%-o@y|I|6j!)4<9efQ_JH^1>4jL}_y+mokDt=<_Q1hrK)=ti0LUIt!M t>1aNnm|`hDFLYGvNYQ7e9Bt{apq_IoMBHcgD5;P1X_}9*!$0B7{0~CDbHe}t diff --git a/test/api/fonts/AdobeVFPrototype.ac.otf b/test/api/fonts/AdobeVFPrototype.ac.otf index a3cd02a0ea4c3d367eba9e15549d0ac9aff51c5d..231c2ac851318487db0f8eb5f78a5309f70f028d 100644 GIT binary patch delta 1601 zcmYjReNa?Y6u*0)3wG36EuG8{khdf%YOH1W9e$t|vXbOPX}J;3I*KT+EGr8SmWADY z=k4cP*kwUQM8HW9rWr>aOGle@bi#DkyDli~B%SV~KwdSHFqyCnH^E z#>UzSJB(Wv8s6_IZ(-~if^5f@{H*+IGw+89?;!lEsmxTGQmn6F3~~sk6&9QJv3mYB zVGH3sJIhQ`>FxBndx`joh%R%H>EmZ}E$eB)3xpS&iAdbadl(z%CagCXTgnqYVkZfo zB0Rp>RKAZ1o5>w_mb!$!rsAT%A67pie2K9M+xC?lusprR42(^kPT!L`)3WDhkS9-! z_tRgT0W=&Mucw}=6EwSbamOTb1A|6E~bWVsM&|hN>3Aprsa3m1xE=2}jBQ2?t3yI(eabQ!`mgfx-TP;o<(F zVZ2@>!Y!)^Iej9mJDLf`0_>>~p|gfGmL~=b*DgN!xqd7h%)RHQ+yp=xl0}LREg9 zwloyAp2A3u2p%`eSl@vPHq{|2*6<2wk44%8a%D{Mpi>p0ei=wH5~{Ny=}uMFWdpia z!XfoFuo8R3G7E;r4e|Ly8AJ2$&KsEV%Z+nCP&AWB8Bjc*EGsfP6j=$D$qEK~`Fv2r z+T^qx2u1DA##+ybO;E7s-I6?au+8T}X*0N5QI>qnYZ#2{3P$@ef$hR&p+pv`Ir1U zzst=R!Rkm8~Xgw@Kd0YG-1GL9hJ~LH>l4W394hN6Tg6-zk zOI;0p4|?Q^7R95uBC;1_K8mE?=gU(zgU`PNPCIRCNJ@tSM~=;8M>z}JOCePK3Vg5_ z)ZN0=wR+furwcE8YrLKS`XjWK%}XILh_VspKzB#9?>JWX(w$|1XEhvb6CpcZ1lD8z z28S!;sjXWF>J4GKv;ZuX&1cb(0WOsS1RaRpZnG!g*Ch>@gRz!6H5~>f6`a@4&f2!B z97`MLfvX|Dce4o1xgtayBB)jPI@mifY5-*=I97w|eiLHNU17AZ2j_7SYGM?+>LO+1 zpwc;Uw{Q>E?iaz92U1fZVkl7Uj8ZMedQbY=HbO()S~%{V1z&X@n1fAAA!--F6)(=J zC?e%GRPONY30I`UY?w9|CL?96o9xwTsmOGQ0UaF!_0!@s)m|DsnM6cc@epT(R1hJQ z29=AzP3Jr$z4Tsjg!I*93K_k$_&lu$s{pqsr%UK^T7opatV;3t=%P1(=SVJ;dYj{^ zP~4zyted8h1Vd!9msU~9p;W0}NJQ31M$Hu$Q5%I`7p+o@5+X&ynmB(XuJ?th6Tu## z&2^9)rdLeSbV&yyYQ^X#sm{=On3Tj@$moxy8MQCg_J1pV_}ub8nIOtu)uhnrKVvM;=>DMQrroR&C?4sYDX0sQjSSxBk?cPQvV}0?9&H{ HS9JdZMDR`_ delta 1441 zcmX|BeQZ-z6u)iPtsYRG7?kj}yR9n>mnempnUI)8CH^s($|OucsdJ=>8^cl7)phOG zz4har*N^vJw|3naV;fruZjAU37>opnEJjgOq6slVf*&#dVV^AK;!P`?EB`s#~8D;5k9!PslI7(!`&p|uL%F?=yG&cwp)7`d+a>nO)c$? zBP?kAmhf%DZHKxX{?6}eHy>sU8c2|JwmRMyGM42OfD?YfNrJLH#(C2JL)hwUcXgM( z$4)c0Y&GHK?T+pv%=9|_F0<0B^suA7_21w7D7j^|j6J^pNXJpv(hX*3Y;~CQ+{n!A zsrB^dUvAYu@h4a4p9gss_Mns$He|EC6K*fL<TuyN?1 zZ61}wv6Gc>z&UX#J9O^Xd8ubij>w^u6va%e8Z@hUyFbPuMr102<*ew@RDTUL2O2yM zFD1$$9Oj_*(ULgy%7_?#Wcpyu+wJe{md|#Or!j@1cKPH;OcKM1a8FlkFd?kTopIy1 zkBG>4C7H_GiDz3bNBg6Z1jbV~@IrncSx<5pz$$Q` z)QM9g1B1~}R4{A5nAiE`NJ3ca>K#3gffR?3Rt-vk`Y`8=B;rDO&Z%U^1~pkI%dKqw z)Vi^L|4ZH2IZR|y385jP(8*zR7l%}Uc=ZvL{Z<%9BsM#vquv=zQ@OInAwX$pRK7P= z2{W0|Y!bcH1aZpGp+8d%zRxMyKoy+2)6#|m9}?Lv4kImyYNAg}#|3j{;dE>g$Ad&~ zlEcs-2R(_YC?ci*_3Y6a93k&%FJ%=bcWHgF${%zGVr|Ku8sO`5v-w@o*p*0JolwOz zF*rLXdgdZ&e*+XPDs5OIf5dU9HhPej1{yhxaq6EY{w7@KcbbUQ0@$eaaSd1 zmyN5|>Y>R^Op;^?1F|G5U6PE61%3YhHu7zVX`%dRN=MiVW;=R>uUkXG3nTeyJmC7GvxIWAWc?*^%?=?UZ}JGM4Z=aqew|KN#;Z zF*eCY*lF0lM8C7Y`Y2~Bhik?x22KL6+5K)5~Zil#$%pUc|kjRt*g zL_NF&XbZjqL0!sQgPqH;b?*iU971%pb~wWJfKFQ(j#|!OOP&Z`4@%h7g)+7_BFdJ? z8t9C*bOxo`nCwNDB0|$DaKuQc%!ibl_M8iPbgzZO%JX0$_PBWtj3$oqtkLYztXqpl zX8rut+3(4lQKSgSpI?$>37xVehpHqQgM&N^lxA&eMjnKtHdk|l_w+U>E_xL;OckCI zl7$pOOotVhjo)7G>%Mf!ugIPdI=28e;Z6y?t3cTdQ8(o&6rhYn95nOQpcK)~?&+y* zx7g5fOoZwVHIczA2smXAMk?sK{8j$OkWRZh&tf-u5BB;b#9%H zK0;?UxD|2`aw7WrOx|EXmoj1s#f~;AnJ_Y~_+0w@oL%dxv9fs)xR0nQFp1EXFG7n` z1jUXofUOIodXU$Gb3G`Ymm${H6G7W%aGesNK1QzXpHehVs-V-iO3JX|fC$z?aI}^n zhJ$skD3xSvu-D(Q6^=A+gj2pbaJBp30&HCgQJV;EwMOkxM2c&)+UYNg)MUbZNL~m@ zND=Fjd^#=Fnhr6jqcl*TtkP718T4`z5GC15oB`&72;mHdB z(SuX>X+>BExLqEokbHPFMBA(Eve!@dy#>6-^P$q$riMa(L%K;G+C~x#lSw{0MWG3W zN@YVLvSu=BZdF8W0{Yx^N&`9&$rIMA{Bc$9k5DIqt%W|Wi>48J%;Zg%a-tCekK*Gh1evhFxXMyf r_a!nmod$=B!;=c<2{dXa6dF>-rLu8#h1%Tz$T$tX!w>H!e4+anZ}mD@ delta 1528 zcmX|BeQZ-z6ulQ0FOPDm3shFe)T+V!pN zTif@}du`u)-P(0yjBRWsun%Hlz-T0ZviN}kAu%BuNbocMsdl)LdEQ8T$$R(x&N=to z-~F9)?*+%1O?+K_eI0p(bQ25N|H`&)j+=EPMF{zmki0Fg?W%e6OZCz}gyio=TVgxn z)AzG3#J?d9*6n=O@y=AoARz@!i1+WTuc@D1b2o|j8^nJ&+nueY&2}##51m6?+1Ttn zOv2{th;JiqI@s>yTd%Kw`Vb+s77ba~0q6T%reGN^zz{$0LPP#8^ECSZLTq<6yF2pU zBc}){T#1-#c6J;l*4Od3uo6Fchn&p^{{6KZ$rWxSMs_!|pDUXmYp$pab9fIq)=3h(a+#vv+$G`>jFBwbd9Qv0+*V6x|FI0aPclAb8 zXz2w|xX0(N<`x#N&M&}M2N+MUc^Iud%jm1e0peVKT?0%77@Y__2zB3cfYu2ha|`bK z4iJtRzj>_a{Op`*t_u3vaZ+zE@;!LaBgK=l80SpqbJitKG$hBuxM20H^TzDt#FZ;C zU5=^{3;?5HkU&&%eSLt@bfki+@p78vE2!cHDaozLX1j(xK1|DK$4CY3PZ;Tgaukv+ z0MS@`tViW+`)WOYm)LenkpMnsv_)mKQ)BeViBh`PHGCo4d-mrk$vY^EawH`wkcpL3 zJp!^eGA8svb{sS6&)GKcF-Bu3y2=$y3!S>gSJ8%GZKurV0FN-lrh$f<5WS8Jp_y`MDJhpV-&d?0KmJTKiCR+(M)mrFJno zu!GT5kWt-_k_YT`2mn=YRR(`c}4@7a_ava z$p*{l$vcfr5c&|ccQe}G2%sgpg>;;=WoAyrMqnt6@+KMW?P1g)F{%I{{ck7Qs-PdU zr+vsOis{lu0~XJB1Y=D}Zxv-lxrwDAjS5ao3Z0W;nolSZ03;d4bUy41 zNJh%&-kGt@(0>q zkwp-2%VC_$XY5ojqc{CE7ab5m<57ImxRfWu=#aaV^7DoB<_+_k<~Q6~mn-`H>+?U2 zgX}c8VBVM{$r1!*Nmkn>84@$t8sQY0LRf9(C>{{8yHANKd_as#E)gB+0f01D+?LYP zKA|rpp47cq=!T}nXD_GT+PZV)@|I3 zLf~Poq;;7ok8cdm>5|aKrx-n=(Qeql!m&zf9Bvyn2;Mq)8Zo(_vO2UhmO^c+S+%iIqXYnpx?}pcE MF?f?J) diff --git a/test/subset/data/expected/full-font/SourceSerifVariable-Roman.default.1FC,21,41,20,62,63.ttf b/test/subset/data/expected/full-font/SourceSerifVariable-Roman.default.1FC,21,41,20,62,63.ttf new file mode 100644 index 0000000000000000000000000000000000000000..1800da8640c0f9375fb1d096e715690071edc05e GIT binary patch literal 5464 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 literal 0 HcmV?d00001 diff --git a/test/subset/data/expected/full-font/SourceSerifVariable-Roman.default.61,62,63.ttf b/test/subset/data/expected/full-font/SourceSerifVariable-Roman.default.61,62,63.ttf new file mode 100644 index 0000000000000000000000000000000000000000..4b538a87d7ba6034f8a9d8ca63a562b1b3cb1297 GIT binary patch literal 5432 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 literal 0 HcmV?d00001 diff --git a/test/subset/data/expected/full-font/SourceSerifVariable-Roman.default.D7,D8,D9,DA,DE.ttf b/test/subset/data/expected/full-font/SourceSerifVariable-Roman.default.D7,D8,D9,DA,DE.ttf new file mode 100644 index 0000000000000000000000000000000000000000..d579c1fa58e714fd604bb7563c4c970faf2c9bdb GIT binary patch literal 5924 zcmbVQdr(}}8UN0?yZ2s}U6#i_c&Q60AsPs;Mf02l6KX033HY-3K0e8O-}I zzptTjYvaK2A726bUod|m^gt+*-*xr3iPGQ2ys)(^)GJPCDc~Q*d|(gg-+$ztv!M5~ ze0%tzJzL&?`ENwRNhDut2mP5F&iozpImoQ(XbbK3*1e$;DVS4L2N21_>YqVt#Qf@x zuKs;#3v>{3wl_cA(;8A5;Rq?m@$hto_VrSpsKopL=7H`|SKDv?`O|+B*>5LGexJ4`SIhADd==$Y!L*7rkE#~r7ZwJC zm4&M+RMjhMJl*GFvqK_tC~Ni26-~|MoA++Jse6-<_emk!-9<%i?(r9zT32^JD*EDm zoe!_O;eoruxBUE;+Mm^7-O|LE{E2)4ke{x@x?+_Oo-1WV`DPicERhBIDKew{O6m2a z04@P;vy_jlh4L~ zal_uGii)PaH&~-6EWTs^ZM%lPRk}1%B4I{AX_bY<6{e9U_9kX8gV)7liCILx7?Iy$ zMJ$Jq&kQ44N`>E>7tC0Skk{k$7gSYU!}b>hIgFKs2H^AkaY$S-l$A9U|Kwi8OGqgY zH1TTI=ANehM{aLyymRP@=EyoTim`VQN0Cx)#O;>KZ~nTr_Gf(s+pfj*_)a zFb44pi&L`5s0F%4Trbi%ntzRli^P-0BP^aQiibtmm>}4RA$gFhl`gKE46U$m6;fMS zRZ;Hq`+U{ufz4Ou`h0Fzt~Yb7Je!%CpXSa^ai^zw{cBnFHOM|}$|_Y=*H^Ke*Q08x zs#W{f`n|a>x6hY*)#j@pTjKYorMpwIQq%IeaE%Bj(PQMG-=TB7mz%;no35t&=vjK6 zUKOgiN^BB$8eBsDVC>fS@4VN&afTG-Gzj9B70Yz4x5QB5HL&q<;*38}q#sRWB(Bh3y2K_BNRg|>53xT;J}$8v z>1J~j_fWaOi6U*PVz=3)Wb3W94FhQFw(2_CLhsR0ITAn5D~Y-QiC-~Q97Y8ZcCv!! zQqKj%G2zjHu@(_QeZeOi@RCO8DC&|}Baewyz~am?I3#3jgwqKP=ZMi*?|W8fzG3tc zbhh%S)!`T7Q)8^l@$h19=0##Jcr93=Yhjg%x2@o@(>hv`RVjp9Z4B#_<|^%gAXTIsW3NhxxR1uN7jcUrJ*1&^H~@@WgNQn})> zV2xf^YAjf%N}FoINtAC}S~r=>ZFgCCmlCi&VZkX>sr|u%)5xKHY{6L+(8f#{4mowF z1q<@%nHDUmN?&8as2{z>f^D=$4_mN(*)PPiRDa6CYm}%$hDM0%3Lf*o{i zQo02vlOt)P1v_a?QcXk8{gKwTKto%kb5Ect6zL4Lgxhwtwcj5OMaoJ_%St!!ZrEV5 z8C*+D#k+dCLfs(ORc~)*?Q9 z67Fm%FA0`Z1%vBKzfiBCyt%Ejy`z8ozWzw4mZ25g2EBNhbb0#av6g!cZC#x$J>lJ- z)4D{cz8sN})!|U<-p`2{SbB~E_`ffuGVJC6{&SXQ*8|#0yQzm-s10xfJw$!f54a0s zfOb$fwSpR4SX47+4oY~0B^TZHPr!Hp?!s}OEnckR}U--!9G*E4)WWvBHQ1GHQDk2v@fqw z4}D9R4e+=Jo^cer;Vz{@>|Y-})e?}awz+5>DDI$Mw}rZZm{VZ#){fq|QIhU(7uD8Gn%RamA zk~JYzTnPGjADBC14Ptvk6utYBV6xcjGVuAmu<8U-baA# z!;kj~=dlyD)NAZi&M{|#Pb02^ub$xm>d4f(B->|HMJra>gWd|QoWE}4L<`~BX4uJ< z8G#)f%cXN@d5^ISTgkp~Zq0Lw>FrpJ&)o<#^3H7s?jhj#q~&Vmx?MUC*rG41{F1L# zGs~yy%2R6jOkbuq!=6r9$(6Ys*)UHp-X$jP%jAnMEA>_6zY6bX|9RLS⩔}8%Cvk zxviHya~ki#{FW?Vr`HYA z7F<*KhJ%{{I8rAs;&$O7y90Aw+Jw!9OOW~YjQ4K5bN2N|dJMt;rH^TR(-3i%5r!nI z)qu+-`5<1zn1xa(AOD%(q~{ad zg_9-4TR1Ds!JT7JM0{f*oFxoDN)uSuCms>ch=US3w$Ok?cJn~ZgT#QNfQ}IQk%EFF z-y0B`Hl%CX@#6&r1Q5mzIL6=_x?^_M0fvc*A$?-P;W&Pr0E!7R^)*9ZGxQZX(}jq| zrX7xB$HosI{&@UCvd80@pP$!UQcd-vWoC|y96n5rL44`R(X<@%aq7g8Lx&E1bV}1s z#Z)O}VlgHZRnzQ>7!+E(DA(=rq^BoOO_i1TckbL;wkBOsl+4Ubzdn8L%*RICiAMIp z!cRUqJTYa7=_d+xax-#WFJ zR$5cPDD-5P%jML?VsvVJY;0^Ci(MSNZgMOkqmxq$s;VwbO-2*3=`&}}Ove(_)3X=n zqf=8eaA|JlLNpe;I6Ww&$MnYqe_Whf8AF%{(8XMQK>86JW0GR|z05cLKlwVLuY>iK zxm@GpGc+?k?sDaz#B}8QF&}~a`kGr?aI}wCnr%BhYoGp zxCw3JCV)9P-ej3@yC>c~aNxlE({A^uEh~po2X!;!85tRa&J5J1(Z)nf>~}AoJAHa| zbQBBCoS#A>hwwQK={KY0hj&@-ShLa x=b<+mSDY@F15tzS4^NzrF3Kb?iZDOZ86AV}3vrywHrv$b=#e8Id@y3@{x1Yf0o?!q literal 0 HcmV?d00001 diff --git a/test/subset/data/expected/full-font/SourceSerifVariable-Roman.drop-hints.1FC,21,41,20,62,63.ttf b/test/subset/data/expected/full-font/SourceSerifVariable-Roman.drop-hints.1FC,21,41,20,62,63.ttf new file mode 100644 index 0000000000000000000000000000000000000000..1800da8640c0f9375fb1d096e715690071edc05e GIT binary patch literal 5464 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 literal 0 HcmV?d00001 diff --git a/test/subset/data/expected/full-font/SourceSerifVariable-Roman.drop-hints.61,62,63.ttf b/test/subset/data/expected/full-font/SourceSerifVariable-Roman.drop-hints.61,62,63.ttf new file mode 100644 index 0000000000000000000000000000000000000000..4b538a87d7ba6034f8a9d8ca63a562b1b3cb1297 GIT binary patch literal 5432 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 literal 0 HcmV?d00001 diff --git a/test/subset/data/expected/full-font/SourceSerifVariable-Roman.drop-hints.D7,D8,D9,DA,DE.ttf b/test/subset/data/expected/full-font/SourceSerifVariable-Roman.drop-hints.D7,D8,D9,DA,DE.ttf new file mode 100644 index 0000000000000000000000000000000000000000..ead6c9c7025d2300d45599abd1a4413f29a45efb GIT binary patch literal 5928 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

@@1cjR-Itj1nM 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

    IMYR7JSL^Lk8dfVf-aIs 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%0AXaqy>1L$hqA#R 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 Gdi_5e^Qme8 literal 0 HcmV?d00001 diff --git a/test/subset/data/tests/full-font.tests b/test/subset/data/tests/full-font.tests index ff195cead..28ae084f0 100644 --- a/test/subset/data/tests/full-font.tests +++ b/test/subset/data/tests/full-font.tests @@ -1,5 +1,6 @@ FONTS: Roboto-Regular.ttf +SourceSerifVariable-Roman.ttf PROFILES: default.txt From 3faaa52aa7e4c72d94657702047e2d3dddbb3292 Mon Sep 17 00:00:00 2001 From: Michiharu Ariza Date: Thu, 21 Mar 2019 09:50:53 -0700 Subject: [PATCH 071/101] regenerated expected full fonts data after fontTools bug 1550 fixed --- ...riable-Roman.drop-hints.D7,D8,D9,DA,DE.ttf | Bin 5928 -> 5924 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/test/subset/data/expected/full-font/SourceSerifVariable-Roman.drop-hints.D7,D8,D9,DA,DE.ttf b/test/subset/data/expected/full-font/SourceSerifVariable-Roman.drop-hints.D7,D8,D9,DA,DE.ttf index ead6c9c7025d2300d45599abd1a4413f29a45efb..d579c1fa58e714fd604bb7563c4c970faf2c9bdb 100644 GIT binary patch delta 334 zcmZ3Xw?uD(c)bAw14Dpsm}5|JSHUI*2Ehg(-^M>!-zfG-<#q-JmI@$0AvnY_q~XaV zS)lwLAb(S0Sz?h=u1XaHgU|&aUoAN|v4HU@J0DQ}4IsZE4JhBydhQiaz5t{@J*P6w z;pX;33=E7s3=B-$(t+|zEM6XEV5k@R0o0_Ek(!ty>VKGxfq@ywXE4hE3UJ?G+X|Em z0rFKca!V=%e=}SH@<9$(%E?boWDa2gGMS$M36b2yiUI~jMiU_a2#~LkmzbNn@YJ-+ z3=FIh3=G^m3i69f{$FG;0(w9IB+tOY5CC+D0n=tt#y~~|26hH^#_a#!{{Lst`2Ue< z3v)7)8UrIxAv*&HW9R>Go4c6O8O1j;>}6zQlxMVMj0Q_GGWEX+ycxZjgGE6Y08D#V Au>b%7 delta 338 zcmZ3Yw?c1%c)bY&14Dpsm}5|JSHUI*2Ei5}-^M>!-zfG-<#q-JmKq>GAvnY_q~XaV zS)lv@Ab(S0Sz?h=u1XaHgU}TqUoAN|v4HU@J0DQ}9U#9U4JhBydhQiaz68imNYANE zbKJAz9s>g-4+8_!u5_UM5{s8d85rt?{s1-UWTYmhi25I9V_;wg@)^uBfCAh%*tP=Y zB7l6AjNFn6!QTv*fP9dHm2&cv6PY7fKz$xLbtj6nCYGjK3=f@C(gF{Lw#Z(`Wb$ib+{XwMi6l4f9J>VMUE?fPaG76oAd Dy?9uy From 5f15fca66206af36cafc9a7b650462b544460d6e Mon Sep 17 00:00:00 2001 From: Michiharu Ariza Date: Thu, 21 Mar 2019 10:18:03 -0700 Subject: [PATCH 072/101] fix short count optimization --- src/hb-ot-layout-common.hh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/hb-ot-layout-common.hh b/src/hb-ot-layout-common.hh index 02c422a86..e7332faf0 100644 --- a/src/hb-ot-layout-common.hh +++ b/src/hb-ot-layout-common.hh @@ -1762,8 +1762,8 @@ struct VarData { unsigned int old = remap.to_old (i); if (unlikely (old >= src->itemCount)) return_trace (false); - int16_t hi = get_item_delta (old, short_count - 1) & 0xFF00; - if (hi != 0 && hi != 0xFF00) goto found_short; + int16_t delta = get_item_delta (old, short_count - 1); + if (-128 <= delta && delta <= 127) goto found_short; } found_short: From 0593a95e28e08a130c87f23c527156ac1d46219b Mon Sep 17 00:00:00 2001 From: Michiharu Ariza Date: Thu, 21 Mar 2019 10:36:53 -0700 Subject: [PATCH 073/101] refix short count --- src/hb-ot-layout-common.hh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hb-ot-layout-common.hh b/src/hb-ot-layout-common.hh index e7332faf0..23d5279a3 100644 --- a/src/hb-ot-layout-common.hh +++ b/src/hb-ot-layout-common.hh @@ -1763,7 +1763,7 @@ struct VarData unsigned int old = remap.to_old (i); if (unlikely (old >= src->itemCount)) return_trace (false); int16_t delta = get_item_delta (old, short_count - 1); - if (-128 <= delta && delta <= 127) goto found_short; + if (delta < -128 || 127 < delta) goto found_short; } found_short: From d2a0149c2999eab315a0582a5e1fa31a2511b869 Mon Sep 17 00:00:00 2001 From: Michiharu Ariza Date: Thu, 21 Mar 2019 18:09:37 -0700 Subject: [PATCH 074/101] added api test for subset VVAR & bug fix added a mod copy of SourceSerifVariable-Roman.ttf with VVAR as a test font --- src/hb-ot-var-hvar-table.hh | 7 +- test/api/Makefile.am | 2 + .../SourceSerifVariable-Roman-VVAR.abc.ttf | Bin 0 -> 5632 bytes ...SerifVariable-Roman-VVAR.ac.retaingids.ttf | Bin 0 -> 5288 bytes .../SourceSerifVariable-Roman-VVAR.ac.ttf | Bin 0 -> 3224 bytes test/api/test-subset-vvar.c | 103 ++++++++++++++++++ 6 files changed, 108 insertions(+), 4 deletions(-) create mode 100644 test/api/fonts/SourceSerifVariable-Roman-VVAR.abc.ttf create mode 100644 test/api/fonts/SourceSerifVariable-Roman-VVAR.ac.retaingids.ttf create mode 100644 test/api/fonts/SourceSerifVariable-Roman-VVAR.ac.ttf create mode 100644 test/api/test-subset-vvar.c diff --git a/src/hb-ot-var-hvar-table.hh b/src/hb-ot-var-hvar-table.hh index 19257f6d0..bf2c1c165 100644 --- a/src/hb-ot-var-hvar-table.hh +++ b/src/hb-ot-var-hvar-table.hh @@ -123,9 +123,8 @@ struct index_map_subset_plan_t { enum index_map_index_t { ADV_INDEX, - LSB_INDEX, - RSB_INDEX, - TSB_INDEX, + LSB_INDEX, /* dual as TSB */ + RSB_INDEX, /* dual as BSB */ VORG_INDEX }; @@ -217,7 +216,7 @@ struct index_map_subset_plan_t 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]]); + unsigned int bit_count = (max_inners[i]==0)? 1: hb_bit_storage (inner_remaps[i][max_inners[i]]); if (bit_count > inner_bit_count) inner_bit_count = bit_count; } diff --git a/test/api/Makefile.am b/test/api/Makefile.am index c5f1f6d8b..eb033811c 100644 --- a/test/api/Makefile.am +++ b/test/api/Makefile.am @@ -52,6 +52,7 @@ TEST_PROGS = \ test-subset-cff2 \ test-subset-gvar \ test-subset-hvar \ + test-subset-vvar \ test-unicode \ test-version \ $(NULL) @@ -68,6 +69,7 @@ test_subset_cff1_LDADD = $(LDADD) $(top_builddir)/src/libharfbuzz-subset.la test_subset_cff2_LDADD = $(LDADD) $(top_builddir)/src/libharfbuzz-subset.la test_subset_gvar_LDADD = $(LDADD) $(top_builddir)/src/libharfbuzz-subset.la test_subset_hvar_LDADD = $(LDADD) $(top_builddir)/src/libharfbuzz-subset.la +test_subset_vvar_LDADD = $(LDADD) $(top_builddir)/src/libharfbuzz-subset.la test_unicode_CPPFLAGS = \ $(AM_CPPFLAGS) \ diff --git a/test/api/fonts/SourceSerifVariable-Roman-VVAR.abc.ttf b/test/api/fonts/SourceSerifVariable-Roman-VVAR.abc.ttf new file mode 100644 index 0000000000000000000000000000000000000000..7d94abcb0b2cd08e99adcc2d59f51cb3371c12e3 GIT binary patch literal 5632 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 Date: Fri, 22 Mar 2019 10:26:48 -0700 Subject: [PATCH 075/101] fix uninitialized memory bug --- src/hb-ot-layout-common.hh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hb-ot-layout-common.hh b/src/hb-ot-layout-common.hh index 23d5279a3..3c621d715 100644 --- a/src/hb-ot-layout-common.hh +++ b/src/hb-ot-layout-common.hh @@ -1762,7 +1762,7 @@ struct VarData { unsigned int old = remap.to_old (i); if (unlikely (old >= src->itemCount)) return_trace (false); - int16_t delta = get_item_delta (old, short_count - 1); + int16_t delta = src->get_item_delta (old, short_count - 1); if (delta < -128 || 127 < delta) goto found_short; } From 94ef1a703ff20614538680587a23cec5f1649189 Mon Sep 17 00:00:00 2001 From: Michiharu Ariza Date: Fri, 22 Mar 2019 11:05:23 -0700 Subject: [PATCH 076/101] add HVAR & VVAR advance width api test --- test/api/test-ot-metrics-tt-var.c | 41 +++++++++++++++++++++++++++++-- 1 file changed, 39 insertions(+), 2 deletions(-) diff --git a/test/api/test-ot-metrics-tt-var.c b/test/api/test-ot-metrics-tt-var.c index 334f3cd93..56d4b1159 100644 --- a/test/api/test-ot-metrics-tt-var.c +++ b/test/api/test-ot-metrics-tt-var.c @@ -62,7 +62,7 @@ test_extents_tt_var (void) } static void -test_advance_tt_var (void) +test_advance_tt_var_nohvar (void) { hb_face_t *face = hb_test_open_font_file ("fonts/SourceSansVariable-Roman-nohvar-41,C1.ttf"); g_assert (face); @@ -97,13 +97,50 @@ test_advance_tt_var (void) hb_font_destroy (font); } +static void +test_advance_tt_var_hvarvvar (void) +{ + hb_face_t *face = hb_test_open_font_file ("fonts/SourceSerifVariable-Roman-VVAR.abc.ttf"); + g_assert (face); + hb_font_t *font = hb_font_create (face); + hb_face_destroy (face); + g_assert (font); + hb_ot_font_set_funcs (font); + + hb_position_t x, y; + hb_font_get_glyph_advance_for_direction(font, 1, HB_DIRECTION_LTR, &x, &y); + + g_assert_cmpint (x, ==, 508); + g_assert_cmpint (y, ==, 0); + + hb_font_get_glyph_advance_for_direction(font, 1, HB_DIRECTION_TTB, &x, &y); + + g_assert_cmpint (x, ==, 0); + g_assert_cmpint (y, ==, -1000); + + float coords[1] = { 700.0f }; + hb_font_set_var_coords_design (font, coords, 1); + hb_font_get_glyph_advance_for_direction(font, 1, HB_DIRECTION_LTR, &x, &y); + + g_assert_cmpint (x, ==, 531); + g_assert_cmpint (y, ==, 0); + + hb_font_get_glyph_advance_for_direction(font, 1, HB_DIRECTION_TTB, &x, &y); + + g_assert_cmpint (x, ==, 0); + g_assert_cmpint (y, ==, -1012); + + hb_font_destroy (font); +} + int main (int argc, char **argv) { hb_test_init (&argc, &argv); hb_test_add (test_extents_tt_var); - hb_test_add (test_advance_tt_var); + hb_test_add (test_advance_tt_var_nohvar); + hb_test_add (test_advance_tt_var_hvarvvar); return hb_test_run (); } From 79a6c258497e80be15245a7b576e34443d9f7bff Mon Sep 17 00:00:00 2001 From: Qunxin Liu Date: Mon, 25 Mar 2019 19:59:37 -0700 Subject: [PATCH 077/101] try to remove deprecated variable from struct definition --- src/hb-subset-plan.cc | 9 +++++---- src/hb-subset-plan.hh | 2 +- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/hb-subset-plan.cc b/src/hb-subset-plan.cc index 49ab9e133..a001ebc1e 100644 --- a/src/hb-subset-plan.cc +++ b/src/hb-subset-plan.cc @@ -207,23 +207,24 @@ hb_subset_plan_create (hb_face_t *face, plan->drop_layout = input->drop_layout; plan->desubroutinize = input->desubroutinize; plan->unicodes = hb_set_create(); - plan->glyphs_deprecated.init(); + //plan->glyphs_deprecated.init(); plan->source = hb_face_reference (face); plan->dest = hb_face_builder_create (); plan->codepoint_to_glyph = hb_map_create(); plan->glyph_map = hb_map_create(); plan->reverse_glyph_map = hb_map_create(); + hb_vector_t glyphs; plan->_glyphset = _populate_gids_to_retain (face, input->unicodes, input->glyphs, !plan->drop_layout, plan->unicodes, plan->codepoint_to_glyph, - &plan->glyphs_deprecated); + &glyphs); _create_old_gid_to_new_gid_map (face, input->retain_gids, - plan->glyphs_deprecated, + glyphs, plan->glyph_map, plan->reverse_glyph_map, &plan->_num_output_glyphs); @@ -242,7 +243,7 @@ hb_subset_plan_destroy (hb_subset_plan_t *plan) if (!hb_object_destroy (plan)) return; hb_set_destroy (plan->unicodes); - plan->glyphs_deprecated.fini (); + //plan->glyphs_deprecated.fini (); hb_face_destroy (plan->source); hb_face_destroy (plan->dest); hb_map_destroy (plan->codepoint_to_glyph); diff --git a/src/hb-subset-plan.hh b/src/hb-subset-plan.hh index 32c19999d..650a40b01 100644 --- a/src/hb-subset-plan.hh +++ b/src/hb-subset-plan.hh @@ -54,7 +54,7 @@ struct hb_subset_plan_t hb_map_t *reverse_glyph_map; // Deprecated members: - hb_vector_t glyphs_deprecated; + //hb_vector_t glyphs_deprecated; // Plan is only good for a specific source/dest so keep them with it hb_face_t *source; From 3147133b6173487c26813a2a406aebd067b53fbf Mon Sep 17 00:00:00 2001 From: Qunxin Liu Date: Tue, 26 Mar 2019 09:15:56 -0700 Subject: [PATCH 078/101] update arguments in_populate_gids_to_retain() and _create_old_gid_to_new_gid_map() so they don't use deprecated variable --- src/hb-subset-plan.cc | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/src/hb-subset-plan.cc b/src/hb-subset-plan.cc index a001ebc1e..3857c2c8c 100644 --- a/src/hb-subset-plan.cc +++ b/src/hb-subset-plan.cc @@ -99,8 +99,7 @@ _populate_gids_to_retain (hb_face_t *face, const hb_set_t *input_glyphs_to_retain, bool close_over_gsub, hb_set_t *unicodes_to_retain, - hb_map_t *codepoint_to_glyph, - hb_vector_t *glyphs) + hb_map_t *codepoint_to_glyph) { OT::cmap::accelerator_t cmap; OT::glyf::accelerator_t glyf; @@ -145,10 +144,10 @@ _populate_gids_to_retain (hb_face_t *face, _remove_invalid_gids (all_gids_to_retain, face->get_num_glyphs ()); - glyphs->alloc (all_gids_to_retain->get_population ()); - gid = HB_SET_VALUE_INVALID; - while (all_gids_to_retain->next (&gid)) - glyphs->push (gid); + //glyphs->alloc (all_gids_to_retain->get_population ()); + //gid = HB_SET_VALUE_INVALID; + //while (all_gids_to_retain->next (&gid)) + //glyphs->push (gid); cff.fini (); glyf.fini (); @@ -160,26 +159,29 @@ _populate_gids_to_retain (hb_face_t *face, static void _create_old_gid_to_new_gid_map (const hb_face_t *face, bool retain_gids, - const hb_vector_t &glyphs, + hb_set_t *all_gids_to_retain, hb_map_t *glyph_map, /* OUT */ hb_map_t *reverse_glyph_map, /* OUT */ unsigned int *num_glyphs /* OUT */) { - for (unsigned int i = 0; i < glyphs.length; i++) { + hb_codepoint_t gid = HB_SET_VALUE_INVALID; + unsigned int length = 0; + for (unsigned int i = 0; all_gids_to_retain->next (&gid); i++) { if (!retain_gids) { - glyph_map->set (glyphs[i], i); - reverse_glyph_map->set (i, glyphs[i]); + glyph_map->set (gid, i); + reverse_glyph_map->set (i, gid); } else { - glyph_map->set (glyphs[i], glyphs[i]); - reverse_glyph_map->set (glyphs[i], glyphs[i]); + glyph_map->set (gid, gid); + reverse_glyph_map->set (gid, gid); } + ++length; } - if (!retain_gids || glyphs.length == 0) + if (!retain_gids || length == 0) { - *num_glyphs = glyphs.length; + *num_glyphs = length; } else { @@ -213,18 +215,16 @@ hb_subset_plan_create (hb_face_t *face, plan->codepoint_to_glyph = hb_map_create(); plan->glyph_map = hb_map_create(); plan->reverse_glyph_map = hb_map_create(); - hb_vector_t glyphs; plan->_glyphset = _populate_gids_to_retain (face, input->unicodes, input->glyphs, !plan->drop_layout, plan->unicodes, - plan->codepoint_to_glyph, - &glyphs); + plan->codepoint_to_glyph); _create_old_gid_to_new_gid_map (face, input->retain_gids, - glyphs, + plan->_glyphset, plan->glyph_map, plan->reverse_glyph_map, &plan->_num_output_glyphs); From 2d9034491eca0a63db82d3801f05c067a5241b7d Mon Sep 17 00:00:00 2001 From: Qunxin Liu Date: Tue, 26 Mar 2019 10:37:24 -0700 Subject: [PATCH 079/101] completely remove lines that are commented out --- src/hb-subset-plan.cc | 6 ------ src/hb-subset-plan.hh | 3 --- 2 files changed, 9 deletions(-) diff --git a/src/hb-subset-plan.cc b/src/hb-subset-plan.cc index 3857c2c8c..8b7231494 100644 --- a/src/hb-subset-plan.cc +++ b/src/hb-subset-plan.cc @@ -144,10 +144,6 @@ _populate_gids_to_retain (hb_face_t *face, _remove_invalid_gids (all_gids_to_retain, face->get_num_glyphs ()); - //glyphs->alloc (all_gids_to_retain->get_population ()); - //gid = HB_SET_VALUE_INVALID; - //while (all_gids_to_retain->next (&gid)) - //glyphs->push (gid); cff.fini (); glyf.fini (); @@ -209,7 +205,6 @@ hb_subset_plan_create (hb_face_t *face, plan->drop_layout = input->drop_layout; plan->desubroutinize = input->desubroutinize; plan->unicodes = hb_set_create(); - //plan->glyphs_deprecated.init(); plan->source = hb_face_reference (face); plan->dest = hb_face_builder_create (); plan->codepoint_to_glyph = hb_map_create(); @@ -243,7 +238,6 @@ hb_subset_plan_destroy (hb_subset_plan_t *plan) if (!hb_object_destroy (plan)) return; hb_set_destroy (plan->unicodes); - //plan->glyphs_deprecated.fini (); hb_face_destroy (plan->source); hb_face_destroy (plan->dest); hb_map_destroy (plan->codepoint_to_glyph); diff --git a/src/hb-subset-plan.hh b/src/hb-subset-plan.hh index 650a40b01..56726d4d0 100644 --- a/src/hb-subset-plan.hh +++ b/src/hb-subset-plan.hh @@ -53,9 +53,6 @@ struct hb_subset_plan_t hb_map_t *glyph_map; hb_map_t *reverse_glyph_map; - // Deprecated members: - //hb_vector_t glyphs_deprecated; - // Plan is only good for a specific source/dest so keep them with it hb_face_t *source; hb_face_t *dest; From 0dd3fdf9d227f9bd79f395078f8e58dcfc32d1bf Mon Sep 17 00:00:00 2001 From: Behdad Esfahbod Date: Mon, 25 Mar 2019 15:08:14 -0700 Subject: [PATCH 080/101] Update ChangeLog generation Let's see if I can make a release on Mac... --- Makefile.am | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile.am b/Makefile.am index eb46ceaa0..f9f6e3630 100644 --- a/Makefile.am +++ b/Makefile.am @@ -36,7 +36,7 @@ ChangeLog: $(srcdir)/ChangeLog $(srcdir)/ChangeLog: $(AM_V_GEN) if test -d "$(top_srcdir)/.git"; then \ (GIT_DIR=$(top_srcdir)/.git \ - $(GIT) log $(CHANGELOG_RANGE) --stat) | fmt --split-only > $@.tmp \ + $(GIT) log $(CHANGELOG_RANGE) --stat) > $@.tmp \ && mv -f $@.tmp "$(srcdir)/ChangeLog" \ || ($(RM) $@.tmp; \ echo Failed to generate ChangeLog, your ChangeLog may be outdated >&2; \ From e5dfffb1ef610a982ed9878fbf3f9ee49cbc3a97 Mon Sep 17 00:00:00 2001 From: Behdad Esfahbod Date: Mon, 25 Mar 2019 15:15:37 -0700 Subject: [PATCH 081/101] [docs] Update --- docs/harfbuzz-docs.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/harfbuzz-docs.xml b/docs/harfbuzz-docs.xml index 27353389d..0c462f38a 100644 --- a/docs/harfbuzz-docs.xml +++ b/docs/harfbuzz-docs.xml @@ -136,6 +136,7 @@ API Index Index of deprecated API + Index of new symbols in 2.2.0 Index of new symbols in 2.1.0 Index of new symbols in 2.0.0 Index of new symbols in 1.9.0 From 96f12377942dbe1c6b1d0ffa7d626d99cb265443 Mon Sep 17 00:00:00 2001 From: Behdad Esfahbod Date: Tue, 26 Mar 2019 16:17:45 -0700 Subject: [PATCH 082/101] [aat] Add missing check to ankr table Isn't absolutely needed. But helps. --- src/hb-aat-layout-ankr-table.hh | 1 + 1 file changed, 1 insertion(+) diff --git a/src/hb-aat-layout-ankr-table.hh b/src/hb-aat-layout-ankr-table.hh index 236e4aaf1..4087b8c1f 100644 --- a/src/hb-aat-layout-ankr-table.hh +++ b/src/hb-aat-layout-ankr-table.hh @@ -76,6 +76,7 @@ struct ankr TRACE_SANITIZE (this); return_trace (likely (c->check_struct (this) && version == 0 && + c->check_range (this, anchorData) && lookupTable.sanitize (c, this, &(this+anchorData)))); } From ec2a5dc859b03ceb92518aa992e4e9c053b30534 Mon Sep 17 00:00:00 2001 From: Behdad Esfahbod Date: Tue, 26 Mar 2019 16:18:03 -0700 Subject: [PATCH 083/101] Use class templates for Null objects This allows partial-instantiating custom Null object for template Lookup. Before, this had to be handcoded per instantiation. Apparently I missed adding one for AAT::ankr.lookupTable, so it was getting the wrong (generic) null for Lookup object, which is wrong and unsafe. Fixes https://bugs.chromium.org/p/chromium/issues/detail?id=944346 --- src/hb-aat-layout-common.hh | 14 +++----- src/hb-null.hh | 31 +++++++++++------- ...minimized-harfbuzz_fuzzer-5748102301614080 | Bin 0 -> 213 bytes 3 files changed, 24 insertions(+), 21 deletions(-) create mode 100644 test/fuzzing/fonts/clusterfuzz-testcase-minimized-harfbuzz_fuzzer-5748102301614080 diff --git a/src/hb-aat-layout-common.hh b/src/hb-aat-layout-common.hh index 27ade28fe..2508276c2 100644 --- a/src/hb-aat-layout-common.hh +++ b/src/hb-aat-layout-common.hh @@ -418,15 +418,11 @@ struct Lookup } /* Close namespace. */ /* Ugly hand-coded null objects for template Lookup<> :(. */ extern HB_INTERNAL const unsigned char _hb_Null_AAT_Lookup[2]; -template <> -/*static*/ inline const AAT::Lookup& Null > () -{ return *reinterpret_cast *> (_hb_Null_AAT_Lookup); } -template <> -/*static*/ inline const AAT::Lookup& Null > () -{ return *reinterpret_cast *> (_hb_Null_AAT_Lookup); } -template <> -/*static*/ inline const AAT::Lookup >& Null > > () -{ return *reinterpret_cast > *> (_hb_Null_AAT_Lookup); } +template +struct Null > { + static AAT::Lookup const & get_null () + { return *reinterpret_cast *> (_hb_Null_AAT_Lookup); } +}; namespace AAT { enum { DELETED_GLYPH = 0xFFFF }; diff --git a/src/hb-null.hh b/src/hb-null.hh index 204c2fefd..baddd99b5 100644 --- a/src/hb-null.hh +++ b/src/hb-null.hh @@ -105,15 +105,18 @@ hb_vector_size_impl_t const _hb_NullPool[(HB_NULL_POOL_SIZE + sizeof (hb_vector_ /* Generic nul-content Null objects. */ template -static inline Type const & Null () { - static_assert (hb_null_size (Type) <= HB_NULL_POOL_SIZE, "Increase HB_NULL_POOL_SIZE."); - return *reinterpret_cast (_hb_NullPool); -} +struct Null { + static Type const & get_null () + { + static_assert (hb_null_size (Type) <= HB_NULL_POOL_SIZE, "Increase HB_NULL_POOL_SIZE."); + return *reinterpret_cast (_hb_NullPool); + } +}; template struct NullHelper { typedef typename hb_remove_const (typename hb_remove_reference (QType)) Type; - static const Type & get_null () { return Null (); } + static const Type & get_null () { return Null::get_null (); } }; #define Null(Type) NullHelper::get_null () @@ -122,9 +125,11 @@ struct NullHelper } /* Close namespace. */ \ extern HB_INTERNAL const unsigned char _hb_Null_##Namespace##_##Type[Namespace::Type::null_size]; \ template <> \ - /*static*/ inline const Namespace::Type& Null () { \ - return *reinterpret_cast (_hb_Null_##Namespace##_##Type); \ - } \ + struct Null { \ + static Namespace::Type const & get_null () { \ + return *reinterpret_cast (_hb_Null_##Namespace##_##Type); \ + } \ + }; \ namespace Namespace { \ static_assert (true, "Just so we take semicolon after.") #define DEFINE_NULL_NAMESPACE_BYTES(Namespace, Type) \ @@ -134,10 +139,12 @@ struct NullHelper #define DECLARE_NULL_INSTANCE(Type) \ extern HB_INTERNAL const Type _hb_Null_##Type; \ template <> \ - /*static*/ inline const Type& Null () { \ - return _hb_Null_##Type; \ - } \ -static_assert (true, "Just so we take semicolon after.") + struct Null { \ + static Type const & get_null () { \ + return _hb_Null_##Type; \ + } \ + }; \ + static_assert (true, "Just so we take semicolon after.") #define DEFINE_NULL_INSTANCE(Type) \ const Type _hb_Null_##Type diff --git a/test/fuzzing/fonts/clusterfuzz-testcase-minimized-harfbuzz_fuzzer-5748102301614080 b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-harfbuzz_fuzzer-5748102301614080 new file mode 100644 index 0000000000000000000000000000000000000000..4cb979d94c68e86cea724b45678f4468e0a02589 GIT binary patch literal 213 zcmZQzWME)mVF+NbWH3(5%Py)1a)H2sf$<-6c4|>YBm-LkkZ+TosH2seSW&>>#1aKo z!63%U3}JvwNMy(an!*OeAk&aQ3Y^^lWH~T0GQf-l@qxzL7BK7uvOwU!1JGUumVXQk dy+9tsSdeiLV?iqL0Tzb$CmFsnGVmxc002Be7w-T7 literal 0 HcmV?d00001 From 49f9359632c78754b6e1eb32f2505b340cde55c8 Mon Sep 17 00:00:00 2001 From: Michiharu Ariza Date: Tue, 26 Mar 2019 17:10:46 -0700 Subject: [PATCH 084/101] add support of anchor point & SCALED/UNSCALED_COMPONENT_OFFSET some code cleanup --- src/hb-ot-glyf-table.hh | 166 ++++++++++++------ src/hb-ot-var-gvar-table.hh | 38 +++- .../fonts/SourceSansVariable-Roman.anchor.ttf | Bin 0 -> 4708 bytes test/api/test-ot-metrics-tt-var.c | 33 ++++ 4 files changed, 179 insertions(+), 58 deletions(-) create mode 100644 test/api/fonts/SourceSansVariable-Roman.anchor.ttf diff --git a/src/hb-ot-glyf-table.hh b/src/hb-ot-glyf-table.hh index d4cae61b5..2c762bd3b 100644 --- a/src/hb-ot-glyf-table.hh +++ b/src/hb-ot-glyf-table.hh @@ -34,6 +34,8 @@ #include "hb-ot-var-gvar-table.hh" #include "hb-subset-glyf.hh" +#include + namespace OT { @@ -174,8 +176,50 @@ struct glyf return size; } - void transform_point (float &x, float &y) const + bool is_anchored () const { return (flags & ARGS_ARE_XY_VALUES) == 0; } + void get_anchor_points (unsigned int &point1, unsigned int &point2) const { + const HBUINT8 *p = &StructAfter (glyphIndex); + if (flags & ARG_1_AND_2_ARE_WORDS) + { + point1 = ((const HBUINT16 *)p)[0]; + point2 = ((const HBUINT16 *)p)[1]; + } + else + { + point1 = p[0]; + point2 = p[1]; + } + } + + void transform_points (contour_point_vector_t &points) const + { + float matrix[4]; + contour_point_t trans; + if (get_transformation (matrix, trans)) + { + if (scaled_offsets ()) + { + points.translate (trans); + points.transform (matrix); + } + else + { + points.transform (matrix); + points.translate (trans); + } + } + } + + protected: + bool scaled_offsets () const + { return (flags & (SCALED_COMPONENT_OFFSET|UNSCALED_COMPONENT_OFFSET)) == SCALED_COMPONENT_OFFSET; } + + bool get_transformation (float (&matrix)[4], contour_point_t &trans) const + { + matrix[0] = matrix[3] = 1.f; + matrix[1] = matrix[2] = 0.f; + int tx, ty; const HBINT8 *p = &StructAfter (glyphIndex); if (flags & ARG_1_AND_2_ARE_WORDS) @@ -190,28 +234,33 @@ struct glyf tx = *p++; ty = *p++; } - if (!(flags & ARGS_ARE_XY_VALUES)) tx = ty = 0; /* TODO: anchor point unsupported for now */ + if (is_anchored ()) tx = ty = 0; + + trans.init ((float)tx, (float)ty); if (flags & WE_HAVE_A_SCALE) { - float scale = ((const F2DOT14*)p)->to_float (); - x *= scale; - y *= scale; + matrix[0] = matrix[3] = ((const F2DOT14*)p)->to_float (); + return true; } else if (flags & WE_HAVE_AN_X_AND_Y_SCALE) { - x *= ((const F2DOT14*)p)[0].to_float (); - y *= ((const F2DOT14*)p)[1].to_float (); + matrix[0] = ((const F2DOT14*)p)[0].to_float (); + matrix[3] = ((const F2DOT14*)p)[1].to_float (); + return true; } else if (flags & WE_HAVE_A_TWO_BY_TWO) { - float x_ = x * ((const F2DOT14*)p)[0].to_float () + y * ((const F2DOT14*)p)[1].to_float (); - y = x * ((const F2DOT14*)p)[2].to_float () + y * ((const F2DOT14*)p)[3].to_float (); - x = x_; + matrix[0] = ((const F2DOT14*)p)[0].to_float (); + matrix[1] = ((const F2DOT14*)p)[1].to_float (); + matrix[2] = ((const F2DOT14*)p)[2].to_float (); + matrix[3] = ((const F2DOT14*)p)[3].to_float (); + return true; } - if (tx | ty) { x += tx; y += ty; } + return tx || ty; } + public: struct Iterator { const char *glyph_start; @@ -362,7 +411,7 @@ struct glyf template static bool read_points (const HBUINT8 *&p /* IN/OUT */, - hb_vector_t &points_ /* IN/OUT */, + contour_point_vector_t &points_ /* IN/OUT */, const range_checker_t &checker) { T coord_setter; @@ -411,7 +460,7 @@ struct glyf * in both cases points trailed with four phantom points */ bool get_contour_points (hb_codepoint_t glyph, - hb_vector_t &points_ /* OUT */, + contour_point_vector_t &points_ /* OUT */, hb_vector_t &end_points_ /* OUT */, const bool phantom_only=false) const { @@ -493,10 +542,10 @@ struct glyf /* 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_array_t phantoms /* OUT */) const + contour_point_vector_t &phantoms /* OUT */) const { - hb_vector_t points; - hb_vector_t end_points; + contour_point_vector_t points; + hb_vector_t end_points; if (unlikely (!get_contour_points (glyph, points, end_points, true/*phantom_only*/))) return false; hb_array_t phantoms_array = points.sub_array (points.length-PHANTOM_COUNT, PHANTOM_COUNT); init_phantom_points (glyph, phantoms_array); @@ -513,9 +562,9 @@ struct glyf if (composite.current->flags & CompositeGlyphHeader::USE_MY_METRICS) { if (unlikely (!get_var_metrics (composite.current->glyphIndex, coords, coord_count, - phantoms.sub_array (0, 2)))) return false; - for (unsigned int j = 0; j < phantoms.length; j++) - composite.current->transform_point (phantoms[j].x, phantoms[j].y); + phantoms))) return false; + + composite.current->transform_points (phantoms); } } while (composite.move_to_next()); return true; @@ -533,28 +582,21 @@ struct glyf max.y = MAX (max.y, p.y); } - void offset (const contour_point_t &p) { min.offset (p); max.offset (p); } - - void merge (const contour_bounds_t &b) - { - if (empty ()) { *this = b; return; } - add (b.min); - add (b.max); - } - bool empty () const { return (min.x >= max.x) || (min.y >= max.y); } 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, + /* Note: Recursively calls itself. + * all_points does not include phantom points + */ + bool get_points_var (hb_codepoint_t glyph, const int *coords, unsigned int coord_count, - contour_bounds_t &bounds) const + contour_point_vector_t &all_points /* OUT */) const { - hb_vector_t points; - hb_vector_t end_points; + contour_point_vector_t points; + hb_vector_t end_points; if (unlikely (!get_contour_points (glyph, points, end_points))) return false; hb_array_t phantoms_array = points.sub_array (points.length-PHANTOM_COUNT, PHANTOM_COUNT); init_phantom_points (glyph, phantoms_array); @@ -565,34 +607,50 @@ struct glyf if (!get_composite (glyph, &composite)) { /* simple glyph */ - for (unsigned int i = 0; i + PHANTOM_COUNT < points.length; i++) - bounds.add (points[i]); /* TODO: need to check ON_CURVE or flatten? */ + if (likely (points.length >= PHANTOM_COUNT)) + all_points.extend (points.sub_array (0, points.length - PHANTOM_COUNT)); } else { /* composite glyph */ do { - contour_bounds_t comp_bounds; - if (unlikely (!get_bounds_var (composite.current->glyphIndex, coords, coord_count, comp_bounds))) return false; + contour_point_vector_t comp_points; + if (unlikely (!get_points_var (composite.current->glyphIndex, coords, coord_count, + comp_points))) return false; - /* Apply offset & scaling */ - composite.current->transform_point (comp_bounds.min.x, comp_bounds.min.y); - composite.current->transform_point (comp_bounds.max.x, comp_bounds.max.y); + /* Apply component transformation & translation */ + composite.current->transform_points (comp_points); - /* Apply offset adjustments from gvar */ - comp_bounds.offset (points[comp_index]); - - bounds.merge (comp_bounds); + /* Apply translatation from gvar */ + comp_points.translate (points[comp_index]); + + if (composite.current->is_anchored ()) + { + unsigned int p1, p2; + composite.current->get_anchor_points (p1, p2); + if (likely (p1 < all_points.length && p2 < comp_points.length)) + { + contour_point_t delta; + delta.init (all_points[p1].x - comp_points[p2].x, + all_points[p1].y - comp_points[p2].y); + + comp_points.translate (delta); + } + } + all_points.extend (comp_points.as_array ()); + comp_index++; } while (composite.move_to_next()); } - /* Shift bounds by the updated left side bearing (vertically too?) */ { - float x_delta = points[points.length - PHANTOM_COUNT + PHANTOM_LEFT].x; - bounds.min.x -= x_delta; - bounds.max.x -= x_delta; + /* Undocumented rasterizer behavior: + * Shift points horizontally by the updated left side bearing + */ + contour_point_t delta; + delta.init (points[points.length - PHANTOM_COUNT + PHANTOM_LEFT].x, 0.f); + if (delta.x != 0.f) all_points.translate (delta); } return true; @@ -602,8 +660,12 @@ struct glyf const int *coords, unsigned int coord_count, hb_glyph_extents_t *extents) const { + contour_point_vector_t all_points; + if (unlikely (!get_points_var (glyph, coords, coord_count, all_points))) return false; + contour_bounds_t bounds; - if (unlikely (!get_bounds_var (glyph, coords, coord_count, bounds))) return false; + for (unsigned int i = 0; i < all_points.length; i++) + bounds.add (all_points[i]); if (bounds.min.x >= bounds.max.x) { @@ -793,11 +855,11 @@ struct glyf bool vertical) const { bool success = false; - hb_vector_t phantoms; + contour_point_vector_t phantoms; phantoms.resize (PHANTOM_COUNT); if (likely (coord_count == gvar_accel.get_axis_count ())) - success = get_var_metrics (glyph, coords, coord_count, phantoms.as_array ()); + success = get_var_metrics (glyph, coords, coord_count, phantoms); if (unlikely (!success)) return vertical? vmtx_accel.get_advance (glyph): hmtx_accel.get_advance (glyph); @@ -810,7 +872,7 @@ struct glyf int get_side_bearing_var (hb_codepoint_t glyph, const int *coords, unsigned int coord_count, bool vertical) const { - hb_vector_t phantoms; + contour_point_vector_t phantoms; phantoms.resize (PHANTOM_COUNT); if (unlikely (!get_var_metrics (glyph, coords, coord_count, phantoms))) diff --git a/src/hb-ot-var-gvar-table.hh b/src/hb-ot-var-gvar-table.hh index 50797bdcd..354edf99f 100644 --- a/src/hb-ot-var-gvar-table.hh +++ b/src/hb-ot-var-gvar-table.hh @@ -31,8 +31,6 @@ #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 @@ -43,14 +41,42 @@ namespace OT { struct contour_point_t { - void init () { flag = 0; x = y = 0.0f; } + void init (float x_=0.f, float y_=0.f) { flag = 0; x = x_; y = y_; } - void offset (const contour_point_t &p) { x += p.x; y += p.y; } + void translate (const contour_point_t &p) { x += p.x; y += p.y; } uint8_t flag; float x, y; }; +struct contour_point_vector_t : hb_vector_t +{ + void extend (const hb_array_t &a) + { + unsigned int old_len = length; + resize (old_len + a.length); + for (unsigned int i = 0; i < a.length; i++) + (*this)[old_len + i] = a[i]; + } + + void transform (const float (&matrix)[4]) + { + for (unsigned int i = 0; i < length; i++) + { + contour_point_t &p = (*this)[i]; + float x_ = p.x * matrix[0] + p.y * matrix[1]; + p.y = p.x * matrix[2] + p.y * matrix[3]; + p.x = x_; + } + } + + void translate (const contour_point_t& delta) + { + for (unsigned int i = 0; i < length; i++) + (*this)[i].translate (delta); + } +}; + struct range_checker_t { range_checker_t (const void *table_, unsigned int start_offset_, unsigned int end_offset_) @@ -564,7 +590,7 @@ struct gvar &iterator)) return false; - hb_vector_t deltas; /* flag is used to indicate referenced point */ + contour_point_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 (); @@ -605,7 +631,7 @@ struct gvar } while (iterator.move_to_next ()); /* infer deltas for unreferenced points */ - hb_vector_t orig_points; + contour_point_vector_t orig_points; orig_points.resize (points.length); for (unsigned int i = 0; i < orig_points.length; i++) orig_points[i] = points[i]; diff --git a/test/api/fonts/SourceSansVariable-Roman.anchor.ttf b/test/api/fonts/SourceSansVariable-Roman.anchor.ttf new file mode 100644 index 0000000000000000000000000000000000000000..4e8dc9db4741fbdf45a1dfe4517a08f4c1e1fab5 GIT binary patch literal 4708 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 literal 0 HcmV?d00001 diff --git a/test/api/test-ot-metrics-tt-var.c b/test/api/test-ot-metrics-tt-var.c index 56d4b1159..3259a7910 100644 --- a/test/api/test-ot-metrics-tt-var.c +++ b/test/api/test-ot-metrics-tt-var.c @@ -133,6 +133,38 @@ test_advance_tt_var_hvarvvar (void) hb_font_destroy (font); } +static void +test_advance_tt_var_anchor (void) +{ + hb_face_t *face = hb_test_open_font_file ("fonts/SourceSansVariable-Roman.anchor.ttf"); + g_assert (face); + hb_font_t *font = hb_font_create (face); + hb_face_destroy (face); + g_assert (font); + hb_ot_font_set_funcs (font); + + hb_glyph_extents_t extents; + hb_bool_t result = hb_font_get_glyph_extents (font, 2, &extents); + g_assert (result); + + g_assert_cmpint (extents.x_bearing, ==, 56); + g_assert_cmpint (extents.y_bearing, ==, 672); + g_assert_cmpint (extents.width, ==, 556); + g_assert_cmpint (extents.height, ==, -684); + + float coords[1] = { 500.0f }; + hb_font_set_var_coords_design (font, coords, 1); + result = hb_font_get_glyph_extents (font, 2, &extents); + g_assert (result); + + g_assert_cmpint (extents.x_bearing, ==, 50); + g_assert_cmpint (extents.y_bearing, ==, 668); + g_assert_cmpint (extents.width, ==, 593); + g_assert_cmpint (extents.height, ==, -680); + + hb_font_destroy (font); +} + int main (int argc, char **argv) { @@ -141,6 +173,7 @@ main (int argc, char **argv) hb_test_add (test_extents_tt_var); hb_test_add (test_advance_tt_var_nohvar); hb_test_add (test_advance_tt_var_hvarvvar); + hb_test_add (test_advance_tt_var_anchor); return hb_test_run (); } From feb712d8d80c48e08f3f715a41400e4ef19b80de Mon Sep 17 00:00:00 2001 From: Michiharu Ariza Date: Tue, 26 Mar 2019 20:35:01 -0700 Subject: [PATCH 085/101] add recursion checks --- src/hb-ot-glyf-table.hh | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/src/hb-ot-glyf-table.hh b/src/hb-ot-glyf-table.hh index 2c762bd3b..4e02c742e 100644 --- a/src/hb-ot-glyf-table.hh +++ b/src/hb-ot-glyf-table.hh @@ -539,10 +539,11 @@ struct glyf read_points (p, points_, checker)); } - /* Note: Recursively calls itself. Who's checking recursively nested composite glyph BTW? */ + /* Note: Recursively calls itself. */ bool get_var_metrics (hb_codepoint_t glyph, const int *coords, unsigned int coord_count, - contour_point_vector_t &phantoms /* OUT */) const + contour_point_vector_t &phantoms /* OUT */, + unsigned int depth) const { contour_point_vector_t points; hb_vector_t end_points; @@ -561,8 +562,9 @@ struct glyf { if (composite.current->flags & CompositeGlyphHeader::USE_MY_METRICS) { - if (unlikely (!get_var_metrics (composite.current->glyphIndex, coords, coord_count, - phantoms))) return false; + if (unlikely (depth >= HB_MAX_NESTING_LEVEL || + !get_var_metrics (composite.current->glyphIndex, coords, coord_count, + phantoms, depth+1))) return false; composite.current->transform_points (phantoms); } @@ -593,7 +595,8 @@ struct glyf */ bool get_points_var (hb_codepoint_t glyph, const int *coords, unsigned int coord_count, - contour_point_vector_t &all_points /* OUT */) const + contour_point_vector_t &all_points /* OUT */, + unsigned int depth) const { contour_point_vector_t points; hb_vector_t end_points; @@ -616,8 +619,9 @@ struct glyf do { contour_point_vector_t comp_points; - if (unlikely (!get_points_var (composite.current->glyphIndex, coords, coord_count, - comp_points))) return false; + if (unlikely (depth >= HB_MAX_NESTING_LEVEL || + !get_points_var (composite.current->glyphIndex, coords, coord_count, + comp_points, depth+1))) return false; /* Apply component transformation & translation */ composite.current->transform_points (comp_points); @@ -661,7 +665,7 @@ struct glyf hb_glyph_extents_t *extents) const { contour_point_vector_t all_points; - if (unlikely (!get_points_var (glyph, coords, coord_count, all_points))) return false; + if (unlikely (!get_points_var (glyph, coords, coord_count, all_points, 0))) return false; contour_bounds_t bounds; for (unsigned int i = 0; i < all_points.length; i++) @@ -859,7 +863,7 @@ struct glyf phantoms.resize (PHANTOM_COUNT); if (likely (coord_count == gvar_accel.get_axis_count ())) - success = get_var_metrics (glyph, coords, coord_count, phantoms); + success = get_var_metrics (glyph, coords, coord_count, phantoms, 0); if (unlikely (!success)) return vertical? vmtx_accel.get_advance (glyph): hmtx_accel.get_advance (glyph); @@ -875,7 +879,7 @@ struct glyf contour_point_vector_t phantoms; phantoms.resize (PHANTOM_COUNT); - if (unlikely (!get_var_metrics (glyph, coords, coord_count, phantoms))) + if (unlikely (!get_var_metrics (glyph, coords, coord_count, phantoms, 0))) return vertical? vmtx_accel.get_side_bearing (glyph): hmtx_accel.get_side_bearing (glyph); return (int)(vertical? -ceilf (phantoms[PHANTOM_TOP].y): floorf (phantoms[PHANTOM_LEFT].x)); From 08e36c5d8be22b3a7e31f33af9452372dafeacc0 Mon Sep 17 00:00:00 2001 From: Ebrahim Byagowi Date: Wed, 27 Mar 2019 16:21:47 +0430 Subject: [PATCH 086/101] [ci] Don't install ragel on cmake build bot images It is not needed anyway --- .circleci/config.yml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 8aa91b21a..364766ae4 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -271,7 +271,6 @@ jobs: - image: dockcross/android-arm steps: - checkout - - run: apt update && apt install ragel - run: cmake -Bbuild -H. -GNinja - run: ninja -Cbuild @@ -280,7 +279,6 @@ jobs: - image: dockcross/browser-asmjs steps: - checkout - - run: apt update && apt install ragel - run: cmake -Bbuild -H. -GNinja - run: ninja -Cbuild @@ -289,7 +287,6 @@ jobs: - image: dockcross/linux-arm64 steps: - checkout - - run: apt update && apt install ragel - run: cmake -Bbuild -H. -GNinja - run: ninja -Cbuild @@ -298,7 +295,6 @@ jobs: - image: dockcross/linux-mips steps: - checkout - - run: apt update && apt install ragel - run: cmake -Bbuild -H. -GNinja - run: ninja -Cbuild @@ -307,7 +303,6 @@ jobs: # - image: dockcross/windows-x64 # steps: # - checkout - # - run: apt update && apt install ragel # - run: cmake -Bbuild -H. -GNinja # - run: ninja -Cbuild From 717181c5943c13a682c719dce10bfc3d9cc47e6b Mon Sep 17 00:00:00 2001 From: Ebrahim Byagowi Date: Wed, 27 Mar 2019 16:38:39 +0430 Subject: [PATCH 087/101] [ci] remove ragel from psvita compile bot --- .circleci/config.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 364766ae4..54385bba2 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -261,7 +261,6 @@ jobs: - image: dockcross/base steps: - checkout - - run: apt update && apt install ragel - run: git clone https://github.com/vitasdk/vdpm && cd vdpm && ./bootstrap-vitasdk.sh - run: ./autogen.sh --prefix=/usr/local/vitasdk/arm-vita-eabi --host=arm-vita-eabi - run: make -j32 From 67175987bd6c90ca2e79e8d604a73e6052e82823 Mon Sep 17 00:00:00 2001 From: Michiharu Ariza Date: Wed, 27 Mar 2019 08:52:46 -0700 Subject: [PATCH 088/101] tweaked recursion checks --- src/hb-ot-glyf-table.hh | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/hb-ot-glyf-table.hh b/src/hb-ot-glyf-table.hh index 4e02c742e..80b959c19 100644 --- a/src/hb-ot-glyf-table.hh +++ b/src/hb-ot-glyf-table.hh @@ -543,8 +543,9 @@ struct glyf bool get_var_metrics (hb_codepoint_t glyph, const int *coords, unsigned int coord_count, contour_point_vector_t &phantoms /* OUT */, - unsigned int depth) const + unsigned int depth=0) const { + if (unlikely (depth++ > HB_MAX_NESTING_LEVEL)) return false; contour_point_vector_t points; hb_vector_t end_points; if (unlikely (!get_contour_points (glyph, points, end_points, true/*phantom_only*/))) return false; @@ -562,9 +563,8 @@ struct glyf { if (composite.current->flags & CompositeGlyphHeader::USE_MY_METRICS) { - if (unlikely (depth >= HB_MAX_NESTING_LEVEL || - !get_var_metrics (composite.current->glyphIndex, coords, coord_count, - phantoms, depth+1))) return false; + if (unlikely (!get_var_metrics (composite.current->glyphIndex, coords, coord_count, + phantoms, depth))) return false; composite.current->transform_points (phantoms); } @@ -596,8 +596,9 @@ struct glyf bool get_points_var (hb_codepoint_t glyph, const int *coords, unsigned int coord_count, contour_point_vector_t &all_points /* OUT */, - unsigned int depth) const + unsigned int depth=0) const { + if (unlikely (depth++ > HB_MAX_NESTING_LEVEL)) return false; contour_point_vector_t points; hb_vector_t end_points; if (unlikely (!get_contour_points (glyph, points, end_points))) return false; @@ -619,9 +620,8 @@ struct glyf do { contour_point_vector_t comp_points; - if (unlikely (depth >= HB_MAX_NESTING_LEVEL || - !get_points_var (composite.current->glyphIndex, coords, coord_count, - comp_points, depth+1))) return false; + if (unlikely (!get_points_var (composite.current->glyphIndex, coords, coord_count, + comp_points))) return false; /* Apply component transformation & translation */ composite.current->transform_points (comp_points); @@ -665,7 +665,7 @@ struct glyf hb_glyph_extents_t *extents) const { contour_point_vector_t all_points; - if (unlikely (!get_points_var (glyph, coords, coord_count, all_points, 0))) return false; + if (unlikely (!get_points_var (glyph, coords, coord_count, all_points))) return false; contour_bounds_t bounds; for (unsigned int i = 0; i < all_points.length; i++) @@ -863,7 +863,7 @@ struct glyf phantoms.resize (PHANTOM_COUNT); if (likely (coord_count == gvar_accel.get_axis_count ())) - success = get_var_metrics (glyph, coords, coord_count, phantoms, 0); + success = get_var_metrics (glyph, coords, coord_count, phantoms); if (unlikely (!success)) return vertical? vmtx_accel.get_advance (glyph): hmtx_accel.get_advance (glyph); @@ -879,7 +879,7 @@ struct glyf contour_point_vector_t phantoms; phantoms.resize (PHANTOM_COUNT); - if (unlikely (!get_var_metrics (glyph, coords, coord_count, phantoms, 0))) + if (unlikely (!get_var_metrics (glyph, coords, coord_count, phantoms))) return vertical? vmtx_accel.get_side_bearing (glyph): hmtx_accel.get_side_bearing (glyph); return (int)(vertical? -ceilf (phantoms[PHANTOM_TOP].y): floorf (phantoms[PHANTOM_LEFT].x)); From cf040c0fef4a049a75a5ec7972f518b9034bdc76 Mon Sep 17 00:00:00 2001 From: Egor Pugin Date: Thu, 28 Mar 2019 19:06:12 +0300 Subject: [PATCH 089/101] Disable unwanted C++ definitions for MSVC. MSVC does not set __cplusplus to the latest standard and also it does not like redefining some keywords. --- src/hb.hh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hb.hh b/src/hb.hh index 5b66ba85d..67f09b981 100644 --- a/src/hb.hh +++ b/src/hb.hh @@ -214,7 +214,7 @@ extern "C" int hb_memalign_impl(void **memptr, size_t alignment, size_t size); * Compiler attributes */ -#if __cplusplus < 201103L +#if __cplusplus < 201103L && !defined(_MSC_VER) #ifndef nullptr #define nullptr NULL From a7eed7e41dba8e583a9c740a4ca7ddf53e77de63 Mon Sep 17 00:00:00 2001 From: punchcutter Date: Wed, 27 Mar 2019 23:12:58 -0700 Subject: [PATCH 090/101] Override USE category for Grantha and Tirhuta visargas to allow marks --- src/gen-use-table.py | 3 ++- src/hb-ot-shape-complex-use-table.cc | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/gen-use-table.py b/src/gen-use-table.py index c725bb2c0..029e66e53 100755 --- a/src/gen-use-table.py +++ b/src/gen-use-table.py @@ -368,7 +368,8 @@ def map_to_use(data): # TODO: These are not in USE's override list that we have, nor are they in Unicode 12.0 if 0xA926 <= U <= 0xA92A: UIPC = Top # TODO: https://github.com/harfbuzz/harfbuzz/pull/1037 - if U == 0x11302: UIPC = Top + # and https://github.com/harfbuzz/harfbuzz/issues/1631 + if U in [0x11302, 0x11303, 0x114C1]: UIPC = Top if U == 0x1171E: UIPC = Left if 0x1CF8 <= U <= 0x1CF9: UIPC = Top diff --git a/src/hb-ot-shape-complex-use-table.cc b/src/hb-ot-shape-complex-use-table.cc index 4de538209..cb5c358cd 100644 --- a/src/hb-ot-shape-complex-use-table.cc +++ b/src/hb-ot-shape-complex-use-table.cc @@ -581,7 +581,7 @@ static const USE_TABLE_ELEMENT_TYPE use_table[] = { /* Grantha */ - /* 11300 */ VMAbv, VMAbv, VMAbv, VMPst, O, B, B, B, B, B, B, B, B, O, O, B, + /* 11300 */ VMAbv, VMAbv, VMAbv, VMAbv, O, B, B, B, B, B, B, B, B, O, O, B, /* 11310 */ B, O, O, B, B, B, B, B, B, B, B, B, B, B, B, B, /* 11320 */ B, B, B, B, B, B, B, B, B, O, B, B, B, B, B, B, /* 11330 */ B, O, B, B, O, B, B, B, B, B, O, CMBlw, CMBlw, B, VPst, VPst, @@ -610,7 +610,7 @@ static const USE_TABLE_ELEMENT_TYPE use_table[] = { /* 11490 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, /* 114A0 */ B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, B, /* 114B0 */ VPst, VPre, VPst, VBlw, VBlw, VBlw, VBlw, VBlw, VBlw, VPre, VAbv, VPst, VPst, VPst, VPst, VMAbv, - /* 114C0 */ VMAbv, VMPst, H, CMBlw, B, O, O, O, O, O, O, O, O, O, O, O, + /* 114C0 */ VMAbv, VMAbv, H, CMBlw, B, O, O, O, O, O, O, O, O, O, O, O, /* 114D0 */ B, B, B, B, B, B, B, B, B, B, O, O, O, O, O, O, #define use_offset_0x11580u 4720 From 8665b9b0a24e4d46e486057d72c0486b9da16523 Mon Sep 17 00:00:00 2001 From: Behdad Esfahbod Date: Thu, 28 Mar 2019 11:11:52 -0700 Subject: [PATCH 091/101] Comment --- src/hb.hh | 1 + 1 file changed, 1 insertion(+) diff --git a/src/hb.hh b/src/hb.hh index 67f09b981..ec24c1ae0 100644 --- a/src/hb.hh +++ b/src/hb.hh @@ -214,6 +214,7 @@ extern "C" int hb_memalign_impl(void **memptr, size_t alignment, size_t size); * Compiler attributes */ +/* https://github.com/harfbuzz/harfbuzz/issues/1634 */ #if __cplusplus < 201103L && !defined(_MSC_VER) #ifndef nullptr From 160b4a2b01e925812fbf0e7db5bc9dcb90dc81cc Mon Sep 17 00:00:00 2001 From: Behdad Esfahbod Date: Thu, 28 Mar 2019 13:44:38 -0700 Subject: [PATCH 092/101] Fix shell syntax error Fixes https://github.com/harfbuzz/harfbuzz/issues/1612 --- src/check-symbols.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/check-symbols.sh b/src/check-symbols.sh index cea868488..423d18613 100755 --- a/src/check-symbols.sh +++ b/src/check-symbols.sh @@ -26,7 +26,7 @@ for soname in harfbuzz harfbuzz-subset harfbuzz-icu harfbuzz-gobject; do symprefix= if test $suffix = dylib; then symprefix=_; fi - EXPORTED_SYMBOLS="`nm "$so" | grep ' [BCDGINRST] .' | grep -v " $symprefix\\($IGNORED_SYMBOLS\\>\\)" | cut -d' ' -f3 | c++filt`" + EXPORTED_SYMBOLS=`nm "$so" | grep ' [BCDGINRST] .' | grep -v " $symprefix\\($IGNORED_SYMBOLS\\>\\)" | cut -d' ' -f3 | c++filt` prefix=$symprefix`basename "$so" | sed 's/libharfbuzz/hb/; s/-/_/g; s/[.].*//'` From 99f0c107f7e1083d3b8ad8354876770f69fddf52 Mon Sep 17 00:00:00 2001 From: Michiharu Ariza Date: Thu, 28 Mar 2019 15:07:49 -0700 Subject: [PATCH 093/101] fixed a fuzzer bug --- src/hb-ot-var-hvar-table.hh | 1 + 1 file changed, 1 insertion(+) diff --git a/src/hb-ot-var-hvar-table.hh b/src/hb-ot-var-hvar-table.hh index bf2c1c165..2b937e72f 100644 --- a/src/hb-ot-var-hvar-table.hh +++ b/src/hb-ot-var-hvar-table.hh @@ -182,6 +182,7 @@ struct index_map_subset_plan_t last_gid = gid; } + if (last_gid == (hb_codepoint_t)-1) return; map_count = last_gid; for (gid = 0; gid < map_count; gid++) { From a548d1da78b506cc6460fdde3715f6ef13ccad48 Mon Sep 17 00:00:00 2001 From: Ebrahim Byagowi Date: Thu, 28 Mar 2019 15:42:45 -0700 Subject: [PATCH 094/101] [ci] Use only CircleCI for macOS (#1637) --- .circleci/config.yml | 5 +++-- .travis.yml | 18 ------------------ 2 files changed, 3 insertions(+), 20 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 54385bba2..af5b478ba 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -27,8 +27,9 @@ jobs: xcode: "10.2.0" steps: - checkout - - run: HOMEBREW_NO_AUTO_UPDATE=1 brew install wget autoconf automake libtool pkg-config ragel freetype glib cairo - - run: ./autogen.sh --with-freetype --with-glib --with-gobject --with-cairo + - run: HOMEBREW_NO_AUTO_UPDATE=1 brew install wget autoconf automake libtool pkg-config ragel freetype glib cairo icu4c + - run: brew link --force icu4c + - run: export PKG_CONFIG_PATH="/usr/local/opt/icu4c/lib/pkgconfig" && ./autogen.sh --with-freetype --with-glib --with-gobject --with-cairo --with-icu --with-coretext - run: make -j4 - run: make check || .ci/fail.sh diff --git a/.travis.yml b/.travis.yml index 681471dc0..703fac25c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -45,16 +45,6 @@ matrix: - make - make check || .ci/fail.sh - - os: osx - compiler: clang - install: - - brew link --force icu4c - script: - - ./autogen.sh - - ./configure $CONFIGURE_OPTS --with-coretext - - make - - make check || .ci/fail.sh - notifications: irc: "irc.freenode.org#harfbuzz" email: harfbuzz-bots-chatter@googlegroups.com @@ -76,14 +66,6 @@ addons: - libicu-dev # for extra unicode functions - libgraphite2-dev # for extra shapers #- libgirepository1.0-dev # for gobject-introspection - homebrew: - packages: - - cairo - - freetype - - glib - - graphite2 - - icu4c - #- gobject-introspection coverity_scan: project: From 485fe06f74d7dce41480d7d6f0dbe0129a7b2a39 Mon Sep 17 00:00:00 2001 From: Michiharu Ariza Date: Thu, 28 Mar 2019 16:17:36 -0700 Subject: [PATCH 095/101] minor --- src/hb-ot-var-hvar-table.hh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hb-ot-var-hvar-table.hh b/src/hb-ot-var-hvar-table.hh index 2b937e72f..ef549c115 100644 --- a/src/hb-ot-var-hvar-table.hh +++ b/src/hb-ot-var-hvar-table.hh @@ -182,7 +182,7 @@ struct index_map_subset_plan_t last_gid = gid; } - if (last_gid == (hb_codepoint_t)-1) return; + if (unlikely (last_gid == (hb_codepoint_t)-1)) return; map_count = last_gid; for (gid = 0; gid < map_count; gid++) { From 7360265e69a8cdaa9f993c36def2860a79cca49f Mon Sep 17 00:00:00 2001 From: Ebrahim Byagowi Date: Thu, 28 Mar 2019 16:57:56 -0700 Subject: [PATCH 096/101] [ci] Tweak macos and psvita bots (#1638) * Add --with-graphite2 to macOS * Add a dummy ragel script for psvita --- .circleci/config.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index af5b478ba..2cf473a05 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -27,9 +27,8 @@ jobs: xcode: "10.2.0" steps: - checkout - - run: HOMEBREW_NO_AUTO_UPDATE=1 brew install wget autoconf automake libtool pkg-config ragel freetype glib cairo icu4c - - run: brew link --force icu4c - - run: export PKG_CONFIG_PATH="/usr/local/opt/icu4c/lib/pkgconfig" && ./autogen.sh --with-freetype --with-glib --with-gobject --with-cairo --with-icu --with-coretext + - run: HOMEBREW_NO_AUTO_UPDATE=1 brew install wget autoconf automake libtool pkg-config ragel freetype glib cairo icu4c graphite2 + - run: export PKG_CONFIG_PATH="/usr/local/opt/icu4c/lib/pkgconfig" && ./autogen.sh --with-freetype --with-glib --with-gobject --with-cairo --with-icu --with-coretext --with-graphite2 - run: make -j4 - run: make check || .ci/fail.sh @@ -263,6 +262,7 @@ jobs: steps: - checkout - run: git clone https://github.com/vitasdk/vdpm && cd vdpm && ./bootstrap-vitasdk.sh + - run: echo "#""!""/bin/true" > /usr/bin/ragel && chmod +x /usr/bin/ragel - run: ./autogen.sh --prefix=/usr/local/vitasdk/arm-vita-eabi --host=arm-vita-eabi - run: make -j32 From 5ab6de7a6fbad4c4a954c2c81d216486a5a14f72 Mon Sep 17 00:00:00 2001 From: Behdad Esfahbod Date: Thu, 28 Mar 2019 20:23:12 -0700 Subject: [PATCH 097/101] [khmer] Add trailing Coeng to syllable grammar Fixes https://github.com/harfbuzz/harfbuzz/issues/1541 --- src/hb-ot-shape-complex-khmer-machine.hh | 248 +++++++++--------- src/hb-ot-shape-complex-khmer-machine.rl | 2 +- ...01ab2ea1cb1a4d3a2783e2675112ef11ae6404.ttf | Bin 0 -> 1500 bytes .../data/in-house/tests/khmer-misc.tests | 1 + 4 files changed, 122 insertions(+), 129 deletions(-) create mode 100644 test/shaping/data/in-house/fonts/ad01ab2ea1cb1a4d3a2783e2675112ef11ae6404.ttf diff --git a/src/hb-ot-shape-complex-khmer-machine.hh b/src/hb-ot-shape-complex-khmer-machine.hh index 2bc8ca650..65e0ffc85 100644 --- a/src/hb-ot-shape-complex-khmer-machine.hh +++ b/src/hb-ot-shape-complex-khmer-machine.hh @@ -35,29 +35,27 @@ #line 36 "hb-ot-shape-complex-khmer-machine.hh" static const unsigned char _khmer_syllable_machine_trans_keys[] = { 5u, 26u, 5u, 21u, 5u, 26u, 5u, 21u, 1u, 16u, 5u, 21u, 5u, 26u, 5u, 21u, - 5u, 26u, 5u, 21u, 1u, 16u, 5u, 21u, 5u, 26u, 5u, 21u, 1u, 16u, 5u, 21u, - 5u, 26u, 5u, 21u, 5u, 26u, 5u, 21u, 5u, 26u, 1u, 16u, 1u, 29u, 5u, 29u, - 5u, 29u, 5u, 29u, 22u, 22u, 5u, 22u, 5u, 29u, 5u, 29u, 5u, 29u, 5u, 26u, - 5u, 29u, 5u, 29u, 22u, 22u, 5u, 22u, 5u, 29u, 5u, 29u, 1u, 16u, 5u, 29u, - 5u, 29u, 0 + 5u, 26u, 5u, 21u, 5u, 21u, 5u, 26u, 5u, 21u, 1u, 16u, 5u, 21u, 5u, 26u, + 5u, 21u, 5u, 26u, 5u, 21u, 5u, 26u, 1u, 29u, 5u, 29u, 5u, 29u, 5u, 29u, + 22u, 22u, 5u, 22u, 5u, 29u, 5u, 29u, 5u, 29u, 1u, 16u, 5u, 26u, 5u, 29u, + 5u, 29u, 22u, 22u, 5u, 22u, 5u, 29u, 5u, 29u, 1u, 16u, 5u, 29u, 5u, 29u, + 0 }; static const char _khmer_syllable_machine_key_spans[] = { 22, 17, 22, 17, 16, 17, 22, 17, - 22, 17, 16, 17, 22, 17, 16, 17, - 22, 17, 22, 17, 22, 16, 29, 25, - 25, 25, 1, 18, 25, 25, 25, 22, - 25, 25, 1, 18, 25, 25, 16, 25, - 25 + 22, 17, 17, 22, 17, 16, 17, 22, + 17, 22, 17, 22, 29, 25, 25, 25, + 1, 18, 25, 25, 25, 16, 22, 25, + 25, 1, 18, 25, 25, 16, 25, 25 }; static const short _khmer_syllable_machine_index_offsets[] = { 0, 23, 41, 64, 82, 99, 117, 140, - 158, 181, 199, 216, 234, 257, 275, 292, - 310, 333, 351, 374, 392, 415, 432, 462, - 488, 514, 540, 542, 561, 587, 613, 639, - 662, 688, 714, 716, 735, 761, 787, 804, - 830 + 158, 181, 199, 217, 240, 258, 275, 293, + 316, 334, 357, 375, 398, 428, 454, 480, + 506, 508, 527, 553, 579, 605, 622, 645, + 671, 697, 699, 718, 744, 770, 787, 813 }; static const char _khmer_syllable_machine_indicies[] = { @@ -85,142 +83,136 @@ static const char _khmer_syllable_machine_indicies[] = { 0, 0, 0, 0, 0, 0, 12, 0, 0, 0, 0, 4, 0, 11, 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 12, 0, 13, - 13, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 13, 0, - 15, 15, 14, 14, 14, 14, 14, 14, - 14, 14, 14, 14, 14, 14, 14, 14, - 16, 14, 15, 15, 17, 17, 17, 17, - 17, 17, 17, 17, 17, 17, 17, 17, - 17, 17, 16, 17, 17, 17, 17, 18, - 17, 19, 19, 17, 17, 17, 17, 17, - 17, 17, 17, 17, 17, 17, 17, 17, - 17, 18, 17, 20, 20, 17, 17, 17, - 17, 17, 17, 17, 17, 17, 17, 17, - 17, 17, 20, 17, 21, 21, 17, 17, - 17, 17, 17, 17, 17, 17, 17, 17, - 17, 17, 17, 17, 22, 17, 23, 23, - 17, 17, 17, 17, 17, 17, 17, 17, - 17, 17, 17, 17, 17, 17, 24, 17, - 17, 17, 17, 18, 17, 23, 23, 17, - 17, 17, 17, 17, 17, 17, 17, 17, - 17, 17, 17, 17, 17, 24, 17, 25, - 25, 17, 17, 17, 17, 17, 17, 17, - 17, 17, 17, 17, 17, 17, 17, 26, - 17, 17, 17, 17, 18, 17, 25, 25, - 17, 17, 17, 17, 17, 17, 17, 17, - 17, 17, 17, 17, 17, 17, 26, 17, - 15, 15, 17, 17, 17, 17, 17, 17, - 17, 17, 17, 17, 17, 17, 17, 27, - 16, 17, 17, 17, 17, 18, 17, 28, - 28, 17, 17, 17, 17, 17, 17, 17, - 17, 17, 17, 17, 17, 17, 28, 17, - 13, 13, 29, 29, 30, 30, 29, 29, - 29, 29, 2, 2, 29, 31, 29, 13, - 29, 29, 29, 29, 16, 20, 29, 29, - 29, 18, 24, 26, 22, 29, 33, 33, - 32, 32, 32, 32, 32, 32, 32, 34, - 32, 32, 32, 32, 32, 2, 3, 6, - 32, 32, 32, 4, 10, 12, 8, 32, - 35, 35, 32, 32, 32, 32, 32, 32, - 32, 36, 32, 32, 32, 32, 32, 32, - 3, 6, 32, 32, 32, 4, 10, 12, - 8, 32, 5, 5, 32, 32, 32, 32, - 32, 32, 32, 36, 32, 32, 32, 32, - 32, 32, 4, 6, 32, 32, 32, 32, - 32, 32, 8, 32, 6, 32, 7, 7, - 32, 32, 32, 32, 32, 32, 32, 36, - 32, 32, 32, 32, 32, 32, 8, 6, - 32, 37, 37, 32, 32, 32, 32, 32, - 32, 32, 36, 32, 32, 32, 32, 32, - 32, 10, 6, 32, 32, 32, 4, 32, - 32, 8, 32, 38, 38, 32, 32, 32, - 32, 32, 32, 32, 36, 32, 32, 32, - 32, 32, 32, 12, 6, 32, 32, 32, - 4, 10, 32, 8, 32, 35, 35, 32, - 32, 32, 32, 32, 32, 32, 34, 32, - 32, 32, 32, 32, 32, 3, 6, 32, - 32, 32, 4, 10, 12, 8, 32, 15, - 15, 39, 39, 39, 39, 39, 39, 39, - 39, 39, 39, 39, 39, 39, 39, 16, - 39, 39, 39, 39, 18, 39, 41, 41, - 40, 40, 40, 40, 40, 40, 40, 42, - 40, 40, 40, 40, 40, 40, 16, 20, - 40, 40, 40, 18, 24, 26, 22, 40, - 19, 19, 40, 40, 40, 40, 40, 40, - 40, 42, 40, 40, 40, 40, 40, 40, - 18, 20, 40, 40, 40, 40, 40, 40, - 22, 40, 20, 40, 21, 21, 40, 40, - 40, 40, 40, 40, 40, 42, 40, 40, - 40, 40, 40, 40, 22, 20, 40, 43, - 43, 40, 40, 40, 40, 40, 40, 40, - 42, 40, 40, 40, 40, 40, 40, 24, - 20, 40, 40, 40, 18, 40, 40, 22, - 40, 44, 44, 40, 40, 40, 40, 40, - 40, 40, 42, 40, 40, 40, 40, 40, - 40, 26, 20, 40, 40, 40, 18, 24, - 40, 22, 40, 28, 28, 39, 39, 39, + 0, 0, 0, 0, 0, 12, 0, 14, + 14, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 15, + 13, 14, 14, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, + 16, 15, 16, 16, 16, 16, 17, 16, + 18, 18, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, + 17, 16, 19, 19, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, + 16, 19, 16, 20, 20, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 21, 16, 22, 22, 16, + 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 23, 16, 16, + 16, 16, 17, 16, 22, 22, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 23, 16, 24, 24, + 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 25, 16, + 16, 16, 16, 17, 16, 24, 24, 16, + 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 25, 16, 14, + 14, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 26, 15, + 16, 16, 16, 16, 17, 16, 28, 28, + 27, 27, 29, 29, 27, 27, 27, 27, + 2, 2, 27, 30, 27, 28, 27, 27, + 27, 27, 15, 19, 27, 27, 27, 17, + 23, 25, 21, 27, 32, 32, 31, 31, + 31, 31, 31, 31, 31, 33, 31, 31, + 31, 31, 31, 2, 3, 6, 31, 31, + 31, 4, 10, 12, 8, 31, 34, 34, + 31, 31, 31, 31, 31, 31, 31, 35, + 31, 31, 31, 31, 31, 31, 3, 6, + 31, 31, 31, 4, 10, 12, 8, 31, + 5, 5, 31, 31, 31, 31, 31, 31, + 31, 35, 31, 31, 31, 31, 31, 31, + 4, 6, 31, 31, 31, 31, 31, 31, + 8, 31, 6, 31, 7, 7, 31, 31, + 31, 31, 31, 31, 31, 35, 31, 31, + 31, 31, 31, 31, 8, 6, 31, 36, + 36, 31, 31, 31, 31, 31, 31, 31, + 35, 31, 31, 31, 31, 31, 31, 10, + 6, 31, 31, 31, 4, 31, 31, 8, + 31, 37, 37, 31, 31, 31, 31, 31, + 31, 31, 35, 31, 31, 31, 31, 31, + 31, 12, 6, 31, 31, 31, 4, 10, + 31, 8, 31, 34, 34, 31, 31, 31, + 31, 31, 31, 31, 33, 31, 31, 31, + 31, 31, 31, 3, 6, 31, 31, 31, + 4, 10, 12, 8, 31, 28, 28, 31, + 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 28, 31, 14, 14, + 38, 38, 38, 38, 38, 38, 38, 38, + 38, 38, 38, 38, 38, 38, 15, 38, + 38, 38, 38, 17, 38, 40, 40, 39, + 39, 39, 39, 39, 39, 39, 41, 39, + 39, 39, 39, 39, 39, 15, 19, 39, + 39, 39, 17, 23, 25, 21, 39, 18, + 18, 39, 39, 39, 39, 39, 39, 39, + 41, 39, 39, 39, 39, 39, 39, 17, + 19, 39, 39, 39, 39, 39, 39, 21, + 39, 19, 39, 20, 20, 39, 39, 39, + 39, 39, 39, 39, 41, 39, 39, 39, + 39, 39, 39, 21, 19, 39, 42, 42, + 39, 39, 39, 39, 39, 39, 39, 41, + 39, 39, 39, 39, 39, 39, 23, 19, + 39, 39, 39, 17, 39, 39, 21, 39, + 43, 43, 39, 39, 39, 39, 39, 39, + 39, 41, 39, 39, 39, 39, 39, 39, + 25, 19, 39, 39, 39, 17, 23, 39, + 21, 39, 44, 44, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, - 39, 39, 28, 39, 45, 45, 40, 40, - 40, 40, 40, 40, 40, 46, 40, 40, - 40, 40, 40, 27, 16, 20, 40, 40, - 40, 18, 24, 26, 22, 40, 41, 41, - 40, 40, 40, 40, 40, 40, 40, 46, - 40, 40, 40, 40, 40, 40, 16, 20, - 40, 40, 40, 18, 24, 26, 22, 40, - 0 + 39, 44, 39, 45, 45, 39, 39, 39, + 39, 39, 39, 39, 30, 39, 39, 39, + 39, 39, 26, 15, 19, 39, 39, 39, + 17, 23, 25, 21, 39, 40, 40, 39, + 39, 39, 39, 39, 39, 39, 30, 39, + 39, 39, 39, 39, 39, 15, 19, 39, + 39, 39, 17, 23, 25, 21, 39, 0 }; static const char _khmer_syllable_machine_trans_targs[] = { - 22, 1, 30, 24, 25, 3, 26, 5, - 27, 7, 28, 9, 29, 23, 22, 11, - 32, 22, 33, 13, 34, 15, 35, 17, - 36, 19, 37, 40, 39, 22, 31, 38, - 22, 0, 10, 2, 4, 6, 8, 22, - 22, 12, 14, 16, 18, 20, 21 + 20, 1, 28, 22, 23, 3, 24, 5, + 25, 7, 26, 9, 27, 20, 10, 31, + 20, 32, 12, 33, 14, 34, 16, 35, + 18, 36, 39, 20, 21, 30, 37, 20, + 0, 29, 2, 4, 6, 8, 20, 20, + 11, 13, 15, 17, 38, 19 }; static const char _khmer_syllable_machine_trans_actions[] = { 1, 0, 2, 2, 2, 0, 0, 0, - 2, 0, 2, 0, 2, 2, 3, 0, - 4, 5, 2, 0, 0, 0, 2, 0, - 2, 0, 2, 4, 4, 8, 9, 0, - 10, 0, 0, 0, 0, 0, 0, 11, - 12, 0, 0, 0, 0, 0, 0 + 2, 0, 2, 0, 2, 3, 0, 4, + 5, 2, 0, 0, 0, 2, 0, 2, + 0, 2, 4, 8, 2, 9, 0, 10, + 0, 0, 0, 0, 0, 0, 11, 12, + 0, 0, 0, 0, 4, 0 }; static const char _khmer_syllable_machine_to_state_actions[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 6, 0, + 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0 + 0, 0, 0, 0, 0, 0, 0, 0 }; static const char _khmer_syllable_machine_from_state_actions[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 7, 0, + 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0 + 0, 0, 0, 0, 0, 0, 0, 0 }; static const unsigned char _khmer_syllable_machine_eof_trans[] = { 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 15, 18, 18, 18, 18, - 18, 18, 18, 18, 18, 18, 0, 33, - 33, 33, 33, 33, 33, 33, 33, 40, - 41, 41, 41, 41, 41, 41, 40, 41, - 41 + 1, 1, 14, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 0, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 39, 40, + 40, 40, 40, 40, 40, 40, 40, 40 }; -static const int khmer_syllable_machine_start = 22; -static const int khmer_syllable_machine_first_final = 22; +static const int khmer_syllable_machine_start = 20; +static const int khmer_syllable_machine_first_final = 20; static const int khmer_syllable_machine_error = -1; -static const int khmer_syllable_machine_en_main = 22; +static const int khmer_syllable_machine_en_main = 20; #line 36 "hb-ot-shape-complex-khmer-machine.rl" @@ -246,7 +238,7 @@ find_syllables (hb_buffer_t *buffer) int cs; hb_glyph_info_t *info = buffer->info; -#line 250 "hb-ot-shape-complex-khmer-machine.hh" +#line 242 "hb-ot-shape-complex-khmer-machine.hh" { cs = khmer_syllable_machine_start; ts = 0; @@ -262,7 +254,7 @@ find_syllables (hb_buffer_t *buffer) unsigned int syllable_serial = 1; -#line 266 "hb-ot-shape-complex-khmer-machine.hh" +#line 258 "hb-ot-shape-complex-khmer-machine.hh" { int _slen; int _trans; @@ -276,7 +268,7 @@ _resume: #line 1 "NONE" {ts = p;} break; -#line 280 "hb-ot-shape-complex-khmer-machine.hh" +#line 272 "hb-ot-shape-complex-khmer-machine.hh" } _keys = _khmer_syllable_machine_trans_keys + (cs<<1); @@ -346,7 +338,7 @@ _eof_trans: #line 76 "hb-ot-shape-complex-khmer-machine.rl" {act = 3;} break; -#line 350 "hb-ot-shape-complex-khmer-machine.hh" +#line 342 "hb-ot-shape-complex-khmer-machine.hh" } _again: @@ -355,7 +347,7 @@ _again: #line 1 "NONE" {ts = 0;} break; -#line 359 "hb-ot-shape-complex-khmer-machine.hh" +#line 351 "hb-ot-shape-complex-khmer-machine.hh" } if ( ++p != pe ) diff --git a/src/hb-ot-shape-complex-khmer-machine.rl b/src/hb-ot-shape-complex-khmer-machine.rl index 4c596ab64..1076a08e8 100644 --- a/src/hb-ot-shape-complex-khmer-machine.rl +++ b/src/hb-ot-shape-complex-khmer-machine.rl @@ -66,7 +66,7 @@ matra_group = VPre? xgroup VBlw? xgroup (joiner?.VAbv)? xgroup VPst?; syllable_tail = xgroup matra_group xgroup (Coeng.c)? ygroup; -broken_cluster = (Coeng.cn)* syllable_tail; +broken_cluster = (Coeng.cn)* (Coeng | syllable_tail); consonant_syllable = (cn|PLACEHOLDER|DOTTEDCIRCLE) broken_cluster; other = any; diff --git a/test/shaping/data/in-house/fonts/ad01ab2ea1cb1a4d3a2783e2675112ef11ae6404.ttf b/test/shaping/data/in-house/fonts/ad01ab2ea1cb1a4d3a2783e2675112ef11ae6404.ttf new file mode 100644 index 0000000000000000000000000000000000000000..8cc4bb0c5a962f4cff6b1d82366f4e25f3f3d701 GIT binary patch literal 1500 zcma)6O>az57=F%~JDsX(X*%jF&Cph)QPmk!L>JZKrV(be@e!fSw9}5X)0s@`n-DCd zA*@JBLSkbfh;(7$4`{GJLg>z>3w9(;qjNpy-g~$a!kIbueV+IGEyo0r4=MO7zaVKh_Ayaq61WR0PiXpOa6M*QPTgCyZ}a1^H+4 z(!p#`0+o12{(!tVlQ@xMKQrW4$U{SkOez%HxQq3kGXITSc6cN|lVWXuoc0Y6g}7B} z9kiw}m%3<8G!l&jBf*+r;Yl*tkxYJ#x5WA936toMi$#HcRj5HDHo-&}4q*VtaS_*; zeZ|V?y5>lDVX(3&;K30O_WuW7?(y;F`CF{lr^*v6T1v{wO1KAH`P7w#FD?*UwdShU zoOQuUZMml{(giPFxmO!r*+p1rkp^0B(?Dv_qk)#)8VC(c4Ss5tY7Hl*-bEkYq8zo9 ziv3!etM@@!B-+M5T{v*4yyY8wzA5GS24lCBeBb))&iA_69f5k|B54tQ?Ilm)Fi?og z)0{VR7!8GwK;ZuOI;$;E|64uPi6LKz>+rMKsNBaC+Z`#o5f*LK&h<;fZ%D}-qgKc{ z0g)MtnWDAob=?c6hS4DEsJ2fe7s(07nNQW3kGs-6ZzXTvD)J&q0jV-r7U0OK#t+S& zE(6%FqKrB8@3!+OEKp76=q1W<#>GCgV${V3)?mWLek{SXi;GY%D$tHBayW@Rj-VfD zjPPJ#tVI)=iA-9v)DI#>)`21PFk1+lsTri~{24WDvlNEuna43cd+E88(Iea;-me6s zmDhGw$=F_ogGbI=krvF;Ivbv8k{!HFXok7R{if&+aZ@l*Wb1l`Y*L#oM!7%&J zvM+UJU0vt-cSUES9ENq9@qb#JwvfUXI68EsY5kV4lHq83yFFKaY3(J{?A1)j)Gpx{ es?0IDyc&F82hy25iq-d;^(@CbmrwOkXRjX%W#R|` literal 0 HcmV?d00001 diff --git a/test/shaping/data/in-house/tests/khmer-misc.tests b/test/shaping/data/in-house/tests/khmer-misc.tests index a7a1c6dd8..1ea76096a 100644 --- a/test/shaping/data/in-house/tests/khmer-misc.tests +++ b/test/shaping/data/in-house/tests/khmer-misc.tests @@ -87,3 +87,4 @@ ../fonts/3998336402905b8be8301ef7f47cf7e050cbb1bd.ttf::U+17A0,U+17D2,U+1782,U+17D2,U+179F,U+17CA,U+17C0:[uni17C1=0+288|uni17A0=0+928|uni17D21782=0@20,-26+0|uni17D2179F.low=0+302|uni17CA=0@-4,30+0|uni17C0.right1.high=0+288] ../fonts/3998336402905b8be8301ef7f47cf7e050cbb1bd.ttf::U+17A0,U+17D2,U+179A,U+17D2,U+179C,U+1784,U+17D2,U+1780:[uni17D2179A=0+287|uni17A0=0+928|uni17D2179C=0@20,-26+0|uni1784=5+635|uni17D21780=5@0,-26+0] ../fonts/3998336402905b8be8301ef7f47cf7e050cbb1bd.ttf::U+17A0,U+17D2,U+179A,U+17D2,U+179C,U+17B6,U+17C6,U+1784:[uni17D2179A=0+287|uni17A017B6=0+1216|uni17D2179C=0@-268,-26+0|uni17C6=0@47,-29+0|uni1784=7+635] +../fonts/ad01ab2ea1cb1a4d3a2783e2675112ef11ae6404.ttf::U+17D2,U+17D2:[uni25CC=0+635|uni17D2=0+0|uni25CC=0+635|uni17D2=0+0] From 21bb80ebf2e20025a196386cee8fd92dd1eb4597 Mon Sep 17 00:00:00 2001 From: Behdad Esfahbod Date: Thu, 28 Mar 2019 20:50:04 -0700 Subject: [PATCH 098/101] [indic] Add back medial-consonant to grammar Fixes https://github.com/harfbuzz/harfbuzz/issues/1592 --- src/hb-ot-shape-complex-indic-machine.hh | 1246 +++++++++-------- src/hb-ot-shape-complex-indic-machine.rl | 5 +- src/hb-ot-shape-complex-indic.cc | 2 +- src/hb-ot-shape-complex-indic.hh | 6 +- ...5c4b05a0a4d67c1a808081ae3d74a9c66509e8.ttf | Bin 0 -> 1924 bytes .../data/in-house/tests/indic-syllable.tests | 2 + 6 files changed, 673 insertions(+), 588 deletions(-) create mode 100644 test/shaping/data/in-house/fonts/f75c4b05a0a4d67c1a808081ae3d74a9c66509e8.ttf diff --git a/src/hb-ot-shape-complex-indic-machine.hh b/src/hb-ot-shape-complex-indic-machine.hh index e2ecfb89c..08b90e913 100644 --- a/src/hb-ot-shape-complex-indic-machine.hh +++ b/src/hb-ot-shape-complex-indic-machine.hh @@ -51,40 +51,40 @@ static const unsigned char _indic_syllable_machine_trans_keys[] = { 5u, 7u, 7u, 7u, 4u, 8u, 6u, 6u, 16u, 16u, 4u, 8u, 6u, 6u, 16u, 16u, 4u, 8u, 6u, 6u, 16u, 16u, 4u, 8u, 6u, 6u, 16u, 16u, 4u, 13u, 4u, 8u, 4u, 13u, 4u, 8u, 4u, 13u, 4u, 8u, 4u, 13u, 4u, 8u, 4u, 13u, 4u, 13u, - 5u, 8u, 8u, 8u, 1u, 19u, 3u, 13u, 3u, 10u, 4u, 10u, 1u, 16u, 3u, 13u, - 3u, 10u, 4u, 10u, 1u, 16u, 3u, 13u, 3u, 10u, 4u, 10u, 1u, 16u, 3u, 13u, - 3u, 10u, 4u, 10u, 1u, 16u, 3u, 13u, 3u, 10u, 4u, 10u, 5u, 10u, 5u, 10u, + 5u, 8u, 8u, 8u, 1u, 19u, 3u, 17u, 3u, 17u, 4u, 17u, 1u, 16u, 3u, 17u, + 3u, 17u, 4u, 17u, 1u, 16u, 3u, 17u, 3u, 17u, 4u, 17u, 1u, 16u, 3u, 17u, + 3u, 17u, 4u, 17u, 1u, 16u, 3u, 17u, 3u, 17u, 4u, 17u, 5u, 10u, 5u, 10u, 5u, 10u, 10u, 10u, 10u, 10u, 10u, 10u, 5u, 10u, 3u, 10u, 5u, 10u, 3u, 10u, 4u, 10u, 5u, 10u, 3u, 10u, 4u, 10u, 5u, 10u, 3u, 10u, 4u, 10u, 5u, 10u, - 3u, 10u, 4u, 10u, 3u, 10u, 3u, 13u, 1u, 16u, 1u, 16u, 1u, 16u, 3u, 10u, - 3u, 13u, 1u, 16u, 1u, 16u, 1u, 16u, 3u, 10u, 3u, 13u, 1u, 16u, 1u, 16u, - 1u, 16u, 3u, 10u, 3u, 13u, 1u, 16u, 1u, 16u, 1u, 16u, 3u, 10u, 3u, 13u, - 3u, 13u, 3u, 10u, 4u, 10u, 1u, 16u, 3u, 13u, 3u, 10u, 4u, 10u, 1u, 16u, - 3u, 13u, 3u, 10u, 4u, 10u, 1u, 16u, 3u, 13u, 3u, 10u, 4u, 10u, 1u, 16u, - 3u, 13u, 3u, 10u, 4u, 10u, 5u, 10u, 5u, 10u, 5u, 10u, 10u, 10u, 10u, 10u, - 10u, 10u, 5u, 10u, 3u, 10u, 5u, 10u, 3u, 10u, 4u, 10u, 5u, 10u, 3u, 10u, - 4u, 10u, 5u, 10u, 3u, 10u, 4u, 10u, 5u, 10u, 3u, 10u, 4u, 10u, 3u, 10u, - 3u, 13u, 1u, 16u, 1u, 16u, 1u, 16u, 3u, 10u, 3u, 13u, 1u, 16u, 1u, 16u, - 1u, 16u, 3u, 10u, 3u, 13u, 1u, 16u, 1u, 16u, 1u, 16u, 3u, 10u, 3u, 13u, - 1u, 16u, 1u, 16u, 1u, 16u, 4u, 8u, 3u, 10u, 3u, 10u, 4u, 10u, 1u, 16u, - 3u, 13u, 3u, 10u, 4u, 10u, 1u, 16u, 3u, 13u, 3u, 10u, 4u, 10u, 1u, 16u, - 3u, 13u, 3u, 10u, 4u, 10u, 1u, 16u, 3u, 13u, 3u, 10u, 4u, 10u, 5u, 10u, - 5u, 10u, 5u, 10u, 10u, 10u, 10u, 10u, 10u, 10u, 5u, 10u, 3u, 10u, 5u, 10u, + 3u, 10u, 4u, 10u, 4u, 10u, 3u, 17u, 3u, 17u, 1u, 16u, 1u, 16u, 1u, 16u, + 3u, 17u, 3u, 17u, 1u, 16u, 1u, 16u, 1u, 16u, 3u, 17u, 3u, 17u, 1u, 16u, + 1u, 16u, 1u, 16u, 3u, 17u, 3u, 17u, 1u, 16u, 1u, 16u, 1u, 16u, 3u, 17u, + 3u, 17u, 3u, 17u, 3u, 17u, 4u, 17u, 1u, 16u, 3u, 17u, 3u, 17u, 4u, 17u, + 1u, 16u, 3u, 17u, 3u, 17u, 4u, 17u, 1u, 16u, 3u, 17u, 3u, 17u, 4u, 17u, + 1u, 16u, 3u, 17u, 3u, 17u, 4u, 17u, 5u, 10u, 5u, 10u, 5u, 10u, 10u, 10u, + 10u, 10u, 10u, 10u, 5u, 10u, 3u, 10u, 5u, 10u, 3u, 10u, 4u, 10u, 5u, 10u, 3u, 10u, 4u, 10u, 5u, 10u, 3u, 10u, 4u, 10u, 5u, 10u, 3u, 10u, 4u, 10u, - 5u, 10u, 3u, 10u, 4u, 10u, 3u, 10u, 3u, 13u, 1u, 16u, 1u, 16u, 1u, 16u, - 3u, 10u, 3u, 13u, 1u, 16u, 1u, 16u, 1u, 16u, 3u, 10u, 3u, 13u, 1u, 16u, - 1u, 16u, 1u, 16u, 3u, 10u, 3u, 13u, 1u, 16u, 1u, 16u, 1u, 16u, 4u, 13u, - 3u, 10u, 4u, 8u, 3u, 13u, 3u, 10u, 4u, 10u, 1u, 16u, 3u, 13u, 3u, 10u, - 4u, 10u, 1u, 16u, 3u, 13u, 3u, 10u, 4u, 10u, 1u, 16u, 3u, 13u, 3u, 10u, - 4u, 10u, 1u, 16u, 3u, 13u, 3u, 10u, 4u, 10u, 5u, 10u, 5u, 10u, 5u, 10u, - 10u, 10u, 10u, 10u, 10u, 10u, 5u, 10u, 3u, 10u, 5u, 10u, 3u, 10u, 4u, 10u, + 4u, 10u, 3u, 17u, 3u, 17u, 1u, 16u, 1u, 16u, 1u, 16u, 3u, 17u, 3u, 17u, + 1u, 16u, 1u, 16u, 1u, 16u, 3u, 17u, 3u, 17u, 1u, 16u, 1u, 16u, 1u, 16u, + 3u, 17u, 3u, 17u, 1u, 16u, 1u, 16u, 1u, 16u, 4u, 8u, 3u, 17u, 3u, 17u, + 4u, 17u, 1u, 16u, 3u, 17u, 3u, 17u, 4u, 17u, 1u, 16u, 3u, 17u, 3u, 17u, + 4u, 17u, 1u, 16u, 3u, 17u, 3u, 17u, 4u, 17u, 1u, 16u, 3u, 17u, 3u, 17u, + 4u, 17u, 5u, 10u, 5u, 10u, 5u, 10u, 10u, 10u, 10u, 10u, 10u, 10u, 5u, 10u, + 3u, 10u, 5u, 10u, 3u, 10u, 4u, 10u, 5u, 10u, 3u, 10u, 4u, 10u, 5u, 10u, + 3u, 10u, 4u, 10u, 5u, 10u, 3u, 10u, 4u, 10u, 4u, 10u, 3u, 17u, 3u, 17u, + 1u, 16u, 1u, 16u, 1u, 16u, 3u, 17u, 3u, 17u, 1u, 16u, 1u, 16u, 1u, 16u, + 3u, 17u, 3u, 17u, 1u, 16u, 1u, 16u, 1u, 16u, 3u, 17u, 3u, 17u, 1u, 16u, + 1u, 16u, 1u, 16u, 4u, 13u, 3u, 17u, 4u, 8u, 3u, 17u, 3u, 17u, 4u, 17u, + 1u, 16u, 3u, 17u, 3u, 17u, 4u, 17u, 1u, 16u, 3u, 17u, 3u, 17u, 4u, 17u, + 1u, 16u, 3u, 17u, 3u, 17u, 4u, 17u, 1u, 16u, 3u, 17u, 3u, 17u, 4u, 17u, + 5u, 10u, 5u, 10u, 5u, 10u, 10u, 10u, 10u, 10u, 10u, 10u, 5u, 10u, 3u, 10u, 5u, 10u, 3u, 10u, 4u, 10u, 5u, 10u, 3u, 10u, 4u, 10u, 5u, 10u, 3u, 10u, - 4u, 10u, 3u, 10u, 3u, 13u, 1u, 16u, 1u, 16u, 1u, 16u, 3u, 10u, 3u, 13u, - 1u, 16u, 1u, 16u, 1u, 16u, 3u, 10u, 3u, 13u, 1u, 16u, 1u, 16u, 1u, 16u, - 3u, 10u, 3u, 13u, 1u, 16u, 1u, 16u, 1u, 16u, 3u, 10u, 1u, 16u, 3u, 13u, - 1u, 16u, 4u, 13u, 5u, 10u, 10u, 10u, 10u, 10u, 10u, 10u, 5u, 10u, 1u, 16u, - 3u, 10u, 5u, 10u, 5u, 10u, 10u, 10u, 10u, 10u, 10u, 10u, 5u, 10u, 1u, 16u, - 0 + 4u, 10u, 5u, 10u, 3u, 10u, 4u, 10u, 4u, 10u, 3u, 17u, 3u, 17u, 1u, 16u, + 1u, 16u, 1u, 16u, 3u, 17u, 3u, 17u, 1u, 16u, 1u, 16u, 1u, 16u, 3u, 17u, + 3u, 17u, 1u, 16u, 1u, 16u, 1u, 16u, 3u, 17u, 3u, 17u, 1u, 16u, 1u, 16u, + 1u, 16u, 3u, 17u, 1u, 17u, 3u, 17u, 1u, 17u, 4u, 13u, 5u, 10u, 10u, 10u, + 10u, 10u, 10u, 10u, 5u, 10u, 1u, 16u, 3u, 10u, 5u, 10u, 5u, 10u, 10u, 10u, + 10u, 10u, 10u, 10u, 5u, 10u, 1u, 16u, 0 }; static const char _indic_syllable_machine_key_spans[] = { @@ -105,39 +105,40 @@ static const char _indic_syllable_machine_key_spans[] = { 3, 1, 5, 1, 1, 5, 1, 1, 5, 1, 1, 5, 1, 1, 10, 5, 10, 5, 10, 5, 10, 5, 10, 10, - 4, 1, 19, 11, 8, 7, 16, 11, - 8, 7, 16, 11, 8, 7, 16, 11, - 8, 7, 16, 11, 8, 7, 6, 6, + 4, 1, 19, 15, 15, 14, 16, 15, + 15, 14, 16, 15, 15, 14, 16, 15, + 15, 14, 16, 15, 15, 14, 6, 6, 6, 1, 1, 1, 6, 8, 6, 8, 7, 6, 8, 7, 6, 8, 7, 6, - 8, 7, 8, 11, 16, 16, 16, 8, - 11, 16, 16, 16, 8, 11, 16, 16, - 16, 8, 11, 16, 16, 16, 8, 11, - 11, 8, 7, 16, 11, 8, 7, 16, - 11, 8, 7, 16, 11, 8, 7, 16, - 11, 8, 7, 6, 6, 6, 1, 1, - 1, 6, 8, 6, 8, 7, 6, 8, - 7, 6, 8, 7, 6, 8, 7, 8, - 11, 16, 16, 16, 8, 11, 16, 16, - 16, 8, 11, 16, 16, 16, 8, 11, - 16, 16, 16, 5, 8, 8, 7, 16, - 11, 8, 7, 16, 11, 8, 7, 16, - 11, 8, 7, 16, 11, 8, 7, 6, - 6, 6, 1, 1, 1, 6, 8, 6, + 8, 7, 7, 15, 15, 16, 16, 16, + 15, 15, 16, 16, 16, 15, 15, 16, + 16, 16, 15, 15, 16, 16, 16, 15, + 15, 15, 15, 14, 16, 15, 15, 14, + 16, 15, 15, 14, 16, 15, 15, 14, + 16, 15, 15, 14, 6, 6, 6, 1, + 1, 1, 6, 8, 6, 8, 7, 6, 8, 7, 6, 8, 7, 6, 8, 7, - 6, 8, 7, 8, 11, 16, 16, 16, - 8, 11, 16, 16, 16, 8, 11, 16, - 16, 16, 8, 11, 16, 16, 16, 10, - 8, 5, 11, 8, 7, 16, 11, 8, - 7, 16, 11, 8, 7, 16, 11, 8, - 7, 16, 11, 8, 7, 6, 6, 6, - 1, 1, 1, 6, 8, 6, 8, 7, + 7, 15, 15, 16, 16, 16, 15, 15, + 16, 16, 16, 15, 15, 16, 16, 16, + 15, 15, 16, 16, 16, 5, 15, 15, + 14, 16, 15, 15, 14, 16, 15, 15, + 14, 16, 15, 15, 14, 16, 15, 15, + 14, 6, 6, 6, 1, 1, 1, 6, + 8, 6, 8, 7, 6, 8, 7, 6, + 8, 7, 6, 8, 7, 7, 15, 15, + 16, 16, 16, 15, 15, 16, 16, 16, + 15, 15, 16, 16, 16, 15, 15, 16, + 16, 16, 10, 15, 5, 15, 15, 14, + 16, 15, 15, 14, 16, 15, 15, 14, + 16, 15, 15, 14, 16, 15, 15, 14, + 6, 6, 6, 1, 1, 1, 6, 8, 6, 8, 7, 6, 8, 7, 6, 8, - 7, 8, 11, 16, 16, 16, 8, 11, - 16, 16, 16, 8, 11, 16, 16, 16, - 8, 11, 16, 16, 16, 8, 16, 11, - 16, 10, 6, 1, 1, 1, 6, 16, - 8, 6, 6, 1, 1, 1, 6, 16 + 7, 6, 8, 7, 7, 15, 15, 16, + 16, 16, 15, 15, 16, 16, 16, 15, + 15, 16, 16, 16, 15, 15, 16, 16, + 16, 15, 17, 15, 17, 10, 6, 1, + 1, 1, 6, 16, 8, 6, 6, 1, + 1, 1, 6, 16 }; static const short _indic_syllable_machine_index_offsets[] = { @@ -158,39 +159,40 @@ static const short _indic_syllable_machine_index_offsets[] = { 528, 532, 534, 540, 542, 544, 550, 552, 554, 560, 562, 564, 570, 572, 574, 585, 591, 602, 608, 619, 625, 636, 642, 653, - 664, 669, 671, 691, 703, 712, 720, 737, - 749, 758, 766, 783, 795, 804, 812, 829, - 841, 850, 858, 875, 887, 896, 904, 911, - 918, 925, 927, 929, 931, 938, 947, 954, - 963, 971, 978, 987, 995, 1002, 1011, 1019, - 1026, 1035, 1043, 1052, 1064, 1081, 1098, 1115, - 1124, 1136, 1153, 1170, 1187, 1196, 1208, 1225, - 1242, 1259, 1268, 1280, 1297, 1314, 1331, 1340, - 1352, 1364, 1373, 1381, 1398, 1410, 1419, 1427, - 1444, 1456, 1465, 1473, 1490, 1502, 1511, 1519, - 1536, 1548, 1557, 1565, 1572, 1579, 1586, 1588, - 1590, 1592, 1599, 1608, 1615, 1624, 1632, 1639, - 1648, 1656, 1663, 1672, 1680, 1687, 1696, 1704, - 1713, 1725, 1742, 1759, 1776, 1785, 1797, 1814, - 1831, 1848, 1857, 1869, 1886, 1903, 1920, 1929, - 1941, 1958, 1975, 1992, 1998, 2007, 2016, 2024, - 2041, 2053, 2062, 2070, 2087, 2099, 2108, 2116, - 2133, 2145, 2154, 2162, 2179, 2191, 2200, 2208, - 2215, 2222, 2229, 2231, 2233, 2235, 2242, 2251, - 2258, 2267, 2275, 2282, 2291, 2299, 2306, 2315, - 2323, 2330, 2339, 2347, 2356, 2368, 2385, 2402, - 2419, 2428, 2440, 2457, 2474, 2491, 2500, 2512, - 2529, 2546, 2563, 2572, 2584, 2601, 2618, 2635, - 2646, 2655, 2661, 2673, 2682, 2690, 2707, 2719, - 2728, 2736, 2753, 2765, 2774, 2782, 2799, 2811, - 2820, 2828, 2845, 2857, 2866, 2874, 2881, 2888, - 2895, 2897, 2899, 2901, 2908, 2917, 2924, 2933, - 2941, 2948, 2957, 2965, 2972, 2981, 2989, 2996, - 3005, 3013, 3022, 3034, 3051, 3068, 3085, 3094, - 3106, 3123, 3140, 3157, 3166, 3178, 3195, 3212, - 3229, 3238, 3250, 3267, 3284, 3301, 3310, 3327, - 3339, 3356, 3367, 3374, 3376, 3378, 3380, 3387, - 3404, 3413, 3420, 3427, 3429, 3431, 3433, 3440 + 664, 669, 671, 691, 707, 723, 738, 755, + 771, 787, 802, 819, 835, 851, 866, 883, + 899, 915, 930, 947, 963, 979, 994, 1001, + 1008, 1015, 1017, 1019, 1021, 1028, 1037, 1044, + 1053, 1061, 1068, 1077, 1085, 1092, 1101, 1109, + 1116, 1125, 1133, 1141, 1157, 1173, 1190, 1207, + 1224, 1240, 1256, 1273, 1290, 1307, 1323, 1339, + 1356, 1373, 1390, 1406, 1422, 1439, 1456, 1473, + 1489, 1505, 1521, 1537, 1552, 1569, 1585, 1601, + 1616, 1633, 1649, 1665, 1680, 1697, 1713, 1729, + 1744, 1761, 1777, 1793, 1808, 1815, 1822, 1829, + 1831, 1833, 1835, 1842, 1851, 1858, 1867, 1875, + 1882, 1891, 1899, 1906, 1915, 1923, 1930, 1939, + 1947, 1955, 1971, 1987, 2004, 2021, 2038, 2054, + 2070, 2087, 2104, 2121, 2137, 2153, 2170, 2187, + 2204, 2220, 2236, 2253, 2270, 2287, 2293, 2309, + 2325, 2340, 2357, 2373, 2389, 2404, 2421, 2437, + 2453, 2468, 2485, 2501, 2517, 2532, 2549, 2565, + 2581, 2596, 2603, 2610, 2617, 2619, 2621, 2623, + 2630, 2639, 2646, 2655, 2663, 2670, 2679, 2687, + 2694, 2703, 2711, 2718, 2727, 2735, 2743, 2759, + 2775, 2792, 2809, 2826, 2842, 2858, 2875, 2892, + 2909, 2925, 2941, 2958, 2975, 2992, 3008, 3024, + 3041, 3058, 3075, 3086, 3102, 3108, 3124, 3140, + 3155, 3172, 3188, 3204, 3219, 3236, 3252, 3268, + 3283, 3300, 3316, 3332, 3347, 3364, 3380, 3396, + 3411, 3418, 3425, 3432, 3434, 3436, 3438, 3445, + 3454, 3461, 3470, 3478, 3485, 3494, 3502, 3509, + 3518, 3526, 3533, 3542, 3550, 3558, 3574, 3590, + 3607, 3624, 3641, 3657, 3673, 3690, 3707, 3724, + 3740, 3756, 3773, 3790, 3807, 3823, 3839, 3856, + 3873, 3890, 3906, 3924, 3940, 3958, 3969, 3976, + 3978, 3980, 3982, 3989, 4006, 4015, 4022, 4029, + 4031, 4033, 4035, 4042 }; static const short _indic_syllable_machine_indicies[] = { @@ -279,410 +281,485 @@ static const short _indic_syllable_machine_indicies[] = { 76, 73, 72, 72, 72, 72, 144, 72, 78, 78, 76, 1, 0, 146, 145, 148, 149, 150, 151, 152, 153, 76, 73, 147, - 154, 155, 155, 144, 147, 156, 157, 147, - 158, 159, 147, 161, 162, 163, 164, 4, - 1, 160, 165, 160, 160, 35, 160, 166, - 162, 167, 167, 4, 1, 160, 165, 160, - 162, 167, 167, 4, 1, 160, 165, 160, - 168, 160, 160, 160, 17, 169, 160, 1, - 160, 165, 160, 160, 160, 160, 160, 168, - 160, 170, 171, 172, 173, 4, 1, 160, - 165, 160, 160, 33, 160, 174, 171, 175, - 175, 4, 1, 160, 165, 160, 171, 175, - 175, 4, 1, 160, 165, 160, 176, 160, - 160, 160, 17, 177, 160, 1, 160, 165, - 160, 160, 160, 160, 160, 176, 160, 178, - 179, 180, 181, 4, 1, 160, 165, 160, - 160, 31, 160, 182, 179, 183, 183, 4, - 1, 160, 165, 160, 179, 183, 183, 4, - 1, 160, 165, 160, 184, 160, 160, 160, - 17, 185, 160, 1, 160, 165, 160, 160, - 160, 160, 160, 184, 160, 186, 187, 188, - 189, 4, 1, 160, 165, 160, 160, 29, - 160, 190, 187, 191, 191, 4, 1, 160, - 165, 160, 187, 191, 191, 4, 1, 160, - 165, 160, 192, 160, 160, 160, 17, 193, - 160, 1, 160, 165, 160, 160, 160, 160, - 160, 192, 160, 194, 195, 196, 197, 4, - 1, 160, 165, 160, 160, 27, 160, 198, - 195, 199, 199, 4, 1, 160, 165, 160, - 195, 199, 199, 4, 1, 160, 165, 160, - 17, 200, 160, 1, 160, 165, 160, 201, - 201, 160, 1, 160, 165, 160, 202, 160, - 160, 203, 160, 165, 160, 165, 160, 204, - 160, 205, 160, 202, 160, 160, 160, 160, - 165, 160, 17, 160, 201, 201, 160, 1, - 160, 165, 160, 201, 200, 160, 1, 160, - 165, 160, 206, 26, 207, 208, 7, 1, - 160, 165, 160, 26, 207, 208, 7, 1, - 160, 165, 160, 207, 207, 7, 1, 160, - 165, 160, 209, 23, 210, 211, 10, 1, - 160, 165, 160, 23, 210, 211, 10, 1, - 160, 165, 160, 210, 210, 10, 1, 160, - 165, 160, 212, 20, 213, 214, 13, 1, - 160, 165, 160, 20, 213, 214, 13, 1, - 160, 165, 160, 213, 213, 13, 1, 160, - 165, 160, 215, 17, 201, 216, 160, 1, - 160, 165, 160, 17, 201, 216, 160, 1, - 160, 165, 160, 194, 195, 199, 199, 4, - 1, 160, 165, 160, 194, 195, 196, 199, - 4, 1, 160, 165, 160, 160, 27, 160, - 192, 160, 217, 160, 201, 201, 160, 1, - 160, 165, 160, 160, 160, 160, 160, 192, - 160, 192, 160, 160, 160, 201, 201, 160, - 1, 160, 165, 160, 160, 160, 160, 160, - 192, 160, 192, 160, 160, 160, 201, 193, - 160, 1, 160, 165, 160, 160, 160, 160, - 160, 192, 160, 186, 187, 191, 191, 4, - 1, 160, 165, 160, 186, 187, 188, 191, - 4, 1, 160, 165, 160, 160, 29, 160, - 184, 160, 218, 160, 201, 201, 160, 1, - 160, 165, 160, 160, 160, 160, 160, 184, - 160, 184, 160, 160, 160, 201, 201, 160, - 1, 160, 165, 160, 160, 160, 160, 160, - 184, 160, 184, 160, 160, 160, 201, 185, - 160, 1, 160, 165, 160, 160, 160, 160, - 160, 184, 160, 178, 179, 183, 183, 4, - 1, 160, 165, 160, 178, 179, 180, 183, - 4, 1, 160, 165, 160, 160, 31, 160, - 176, 160, 219, 160, 201, 201, 160, 1, - 160, 165, 160, 160, 160, 160, 160, 176, - 160, 176, 160, 160, 160, 201, 201, 160, - 1, 160, 165, 160, 160, 160, 160, 160, - 176, 160, 176, 160, 160, 160, 201, 177, - 160, 1, 160, 165, 160, 160, 160, 160, - 160, 176, 160, 170, 171, 175, 175, 4, - 1, 160, 165, 160, 170, 171, 172, 175, - 4, 1, 160, 165, 160, 160, 33, 160, - 168, 160, 220, 160, 201, 201, 160, 1, - 160, 165, 160, 160, 160, 160, 160, 168, - 160, 168, 160, 160, 160, 201, 201, 160, - 1, 160, 165, 160, 160, 160, 160, 160, - 168, 160, 168, 160, 160, 160, 201, 169, - 160, 1, 160, 165, 160, 160, 160, 160, - 160, 168, 160, 161, 162, 167, 167, 4, - 1, 160, 165, 160, 161, 162, 163, 167, - 4, 1, 160, 165, 160, 160, 35, 160, - 222, 223, 224, 225, 40, 37, 221, 226, - 221, 221, 71, 221, 227, 223, 228, 225, - 40, 37, 221, 226, 221, 223, 228, 225, - 40, 37, 221, 226, 221, 229, 221, 221, - 221, 53, 230, 221, 37, 221, 226, 221, - 221, 221, 221, 221, 229, 221, 231, 232, - 233, 234, 40, 37, 221, 226, 221, 221, - 69, 221, 235, 232, 236, 236, 40, 37, - 221, 226, 221, 232, 236, 236, 40, 37, - 221, 226, 221, 237, 221, 221, 221, 53, - 238, 221, 37, 221, 226, 221, 221, 221, - 221, 221, 237, 221, 239, 240, 241, 242, - 40, 37, 221, 226, 221, 221, 67, 221, - 243, 240, 244, 244, 40, 37, 221, 226, - 221, 240, 244, 244, 40, 37, 221, 226, - 221, 245, 221, 221, 221, 53, 246, 221, - 37, 221, 226, 221, 221, 221, 221, 221, - 245, 221, 247, 248, 249, 250, 40, 37, - 221, 226, 221, 221, 65, 221, 251, 248, - 252, 252, 40, 37, 221, 226, 221, 248, - 252, 252, 40, 37, 221, 226, 221, 253, - 221, 221, 221, 53, 254, 221, 37, 221, - 226, 221, 221, 221, 221, 221, 253, 221, - 255, 256, 257, 258, 40, 37, 221, 226, - 221, 221, 63, 221, 259, 256, 260, 260, - 40, 37, 221, 226, 221, 256, 260, 260, - 40, 37, 221, 226, 221, 53, 261, 221, - 37, 221, 226, 221, 262, 262, 221, 37, - 221, 226, 221, 263, 221, 221, 264, 221, - 226, 221, 226, 221, 265, 221, 266, 221, - 263, 221, 221, 221, 221, 226, 221, 53, - 221, 262, 262, 221, 37, 221, 226, 221, - 262, 261, 221, 37, 221, 226, 221, 267, - 62, 268, 269, 43, 37, 221, 226, 221, - 62, 268, 269, 43, 37, 221, 226, 221, - 268, 268, 43, 37, 221, 226, 221, 270, - 59, 271, 272, 46, 37, 221, 226, 221, - 59, 271, 272, 46, 37, 221, 226, 221, - 271, 271, 46, 37, 221, 226, 221, 273, - 56, 274, 275, 49, 37, 221, 226, 221, - 56, 274, 275, 49, 37, 221, 226, 221, - 274, 274, 49, 37, 221, 226, 221, 276, - 53, 262, 277, 221, 37, 221, 226, 221, - 53, 262, 277, 221, 37, 221, 226, 221, - 255, 256, 260, 260, 40, 37, 221, 226, - 221, 255, 256, 257, 260, 40, 37, 221, - 226, 221, 221, 63, 221, 253, 221, 278, - 221, 262, 262, 221, 37, 221, 226, 221, - 221, 221, 221, 221, 253, 221, 253, 221, - 221, 221, 262, 262, 221, 37, 221, 226, - 221, 221, 221, 221, 221, 253, 221, 253, - 221, 221, 221, 262, 254, 221, 37, 221, - 226, 221, 221, 221, 221, 221, 253, 221, - 247, 248, 252, 252, 40, 37, 221, 226, - 221, 247, 248, 249, 252, 40, 37, 221, - 226, 221, 221, 65, 221, 245, 221, 279, - 221, 262, 262, 221, 37, 221, 226, 221, - 221, 221, 221, 221, 245, 221, 245, 221, - 221, 221, 262, 262, 221, 37, 221, 226, - 221, 221, 221, 221, 221, 245, 221, 245, - 221, 221, 221, 262, 246, 221, 37, 221, - 226, 221, 221, 221, 221, 221, 245, 221, - 239, 240, 244, 244, 40, 37, 221, 226, - 221, 239, 240, 241, 244, 40, 37, 221, - 226, 221, 221, 67, 221, 237, 221, 280, - 221, 262, 262, 221, 37, 221, 226, 221, - 221, 221, 221, 221, 237, 221, 237, 221, - 221, 221, 262, 262, 221, 37, 221, 226, - 221, 221, 221, 221, 221, 237, 221, 237, - 221, 221, 221, 262, 238, 221, 37, 221, - 226, 221, 221, 221, 221, 221, 237, 221, - 231, 232, 236, 236, 40, 37, 221, 226, - 221, 231, 232, 233, 236, 40, 37, 221, - 226, 221, 221, 69, 221, 229, 221, 281, - 221, 262, 262, 221, 37, 221, 226, 221, - 221, 221, 221, 221, 229, 221, 229, 221, - 221, 221, 262, 262, 221, 37, 221, 226, - 221, 221, 221, 221, 221, 229, 221, 229, - 221, 221, 221, 262, 230, 221, 37, 221, - 226, 221, 221, 221, 221, 221, 229, 221, - 70, 39, 39, 40, 37, 221, 222, 223, - 228, 225, 40, 37, 221, 226, 221, 283, - 151, 284, 284, 76, 73, 282, 154, 282, - 151, 284, 284, 76, 73, 282, 154, 282, - 285, 282, 282, 282, 90, 286, 282, 73, - 282, 154, 282, 282, 282, 282, 282, 285, - 282, 287, 288, 289, 290, 76, 73, 282, - 154, 282, 282, 106, 282, 291, 288, 292, - 292, 76, 73, 282, 154, 282, 288, 292, - 292, 76, 73, 282, 154, 282, 293, 282, - 282, 282, 90, 294, 282, 73, 282, 154, - 282, 282, 282, 282, 282, 293, 282, 295, - 296, 297, 298, 76, 73, 282, 154, 282, - 282, 104, 282, 299, 296, 300, 300, 76, - 73, 282, 154, 282, 296, 300, 300, 76, - 73, 282, 154, 282, 301, 282, 282, 282, - 90, 302, 282, 73, 282, 154, 282, 282, - 282, 282, 282, 301, 282, 303, 304, 305, - 306, 76, 73, 282, 154, 282, 282, 102, - 282, 307, 304, 308, 308, 76, 73, 282, - 154, 282, 304, 308, 308, 76, 73, 282, - 154, 282, 309, 282, 282, 282, 90, 310, - 282, 73, 282, 154, 282, 282, 282, 282, - 282, 309, 282, 311, 312, 313, 314, 76, - 73, 282, 154, 282, 282, 100, 282, 315, - 312, 316, 316, 76, 73, 282, 154, 282, - 312, 316, 316, 76, 73, 282, 154, 282, - 90, 317, 282, 73, 282, 154, 282, 318, - 318, 282, 73, 282, 154, 282, 319, 282, - 282, 320, 282, 154, 282, 154, 282, 321, - 282, 322, 282, 319, 282, 282, 282, 282, - 154, 282, 90, 282, 318, 318, 282, 73, - 282, 154, 282, 318, 317, 282, 73, 282, - 154, 282, 323, 99, 324, 325, 80, 73, - 282, 154, 282, 99, 324, 325, 80, 73, - 282, 154, 282, 324, 324, 80, 73, 282, - 154, 282, 326, 96, 327, 328, 83, 73, - 282, 154, 282, 96, 327, 328, 83, 73, - 282, 154, 282, 327, 327, 83, 73, 282, - 154, 282, 329, 93, 330, 331, 86, 73, - 282, 154, 282, 93, 330, 331, 86, 73, - 282, 154, 282, 330, 330, 86, 73, 282, - 154, 282, 332, 90, 318, 333, 282, 73, - 282, 154, 282, 90, 318, 333, 282, 73, - 282, 154, 282, 311, 312, 316, 316, 76, - 73, 282, 154, 282, 311, 312, 313, 316, - 76, 73, 282, 154, 282, 282, 100, 282, - 309, 282, 334, 282, 318, 318, 282, 73, - 282, 154, 282, 282, 282, 282, 282, 309, - 282, 309, 282, 282, 282, 318, 318, 282, - 73, 282, 154, 282, 282, 282, 282, 282, - 309, 282, 309, 282, 282, 282, 318, 310, - 282, 73, 282, 154, 282, 282, 282, 282, - 282, 309, 282, 303, 304, 308, 308, 76, - 73, 282, 154, 282, 303, 304, 305, 308, - 76, 73, 282, 154, 282, 282, 102, 282, - 301, 282, 335, 282, 318, 318, 282, 73, - 282, 154, 282, 282, 282, 282, 282, 301, - 282, 301, 282, 282, 282, 318, 318, 282, - 73, 282, 154, 282, 282, 282, 282, 282, - 301, 282, 301, 282, 282, 282, 318, 302, - 282, 73, 282, 154, 282, 282, 282, 282, - 282, 301, 282, 295, 296, 300, 300, 76, - 73, 282, 154, 282, 295, 296, 297, 300, - 76, 73, 282, 154, 282, 282, 104, 282, - 293, 282, 336, 282, 318, 318, 282, 73, - 282, 154, 282, 282, 282, 282, 282, 293, - 282, 293, 282, 282, 282, 318, 318, 282, - 73, 282, 154, 282, 282, 282, 282, 282, - 293, 282, 293, 282, 282, 282, 318, 294, - 282, 73, 282, 154, 282, 282, 282, 282, - 282, 293, 282, 287, 288, 292, 292, 76, - 73, 282, 154, 282, 287, 288, 289, 292, - 76, 73, 282, 154, 282, 282, 106, 282, - 285, 282, 337, 282, 318, 318, 282, 73, - 282, 154, 282, 282, 282, 282, 282, 285, - 282, 285, 282, 282, 282, 318, 318, 282, - 73, 282, 154, 282, 282, 282, 282, 282, - 285, 282, 285, 282, 282, 282, 318, 286, - 282, 73, 282, 154, 282, 282, 282, 282, - 282, 285, 282, 107, 75, 75, 76, 73, - 338, 338, 338, 338, 144, 338, 150, 151, - 284, 284, 76, 73, 282, 154, 282, 107, - 75, 75, 76, 73, 338, 340, 341, 342, - 343, 112, 109, 339, 344, 339, 339, 143, - 339, 345, 341, 343, 343, 112, 109, 339, - 344, 339, 341, 343, 343, 112, 109, 339, - 344, 339, 346, 339, 339, 339, 125, 347, - 339, 109, 339, 344, 339, 339, 339, 339, - 339, 346, 339, 348, 349, 350, 351, 112, - 109, 339, 344, 339, 339, 141, 339, 352, - 349, 353, 353, 112, 109, 339, 344, 339, - 349, 353, 353, 112, 109, 339, 344, 339, - 354, 339, 339, 339, 125, 355, 339, 109, - 339, 344, 339, 339, 339, 339, 339, 354, - 339, 356, 357, 358, 359, 112, 109, 339, - 344, 339, 339, 139, 339, 360, 357, 361, - 361, 112, 109, 339, 344, 339, 357, 361, - 361, 112, 109, 339, 344, 339, 362, 339, - 339, 339, 125, 363, 339, 109, 339, 344, - 339, 339, 339, 339, 339, 362, 339, 364, - 365, 366, 367, 112, 109, 339, 344, 339, - 339, 137, 339, 368, 365, 369, 369, 112, - 109, 339, 344, 339, 365, 369, 369, 112, - 109, 339, 344, 339, 370, 339, 339, 339, - 125, 371, 339, 109, 339, 344, 339, 339, - 339, 339, 339, 370, 339, 372, 373, 374, - 375, 112, 109, 339, 344, 339, 339, 135, - 339, 376, 373, 377, 377, 112, 109, 339, - 344, 339, 373, 377, 377, 112, 109, 339, - 344, 339, 125, 378, 339, 109, 339, 344, - 339, 379, 379, 339, 109, 339, 344, 339, - 380, 339, 339, 381, 339, 344, 339, 344, - 339, 382, 339, 383, 339, 380, 339, 339, - 339, 339, 344, 339, 125, 339, 379, 379, - 339, 109, 339, 344, 339, 379, 378, 339, - 109, 339, 344, 339, 384, 134, 385, 386, - 115, 109, 339, 344, 339, 134, 385, 386, - 115, 109, 339, 344, 339, 385, 385, 115, - 109, 339, 344, 339, 387, 131, 388, 389, - 118, 109, 339, 344, 339, 131, 388, 389, - 118, 109, 339, 344, 339, 388, 388, 118, - 109, 339, 344, 339, 390, 128, 391, 392, - 121, 109, 339, 344, 339, 128, 391, 392, - 121, 109, 339, 344, 339, 391, 391, 121, - 109, 339, 344, 339, 393, 125, 379, 394, - 339, 109, 339, 344, 339, 125, 379, 394, - 339, 109, 339, 344, 339, 372, 373, 377, - 377, 112, 109, 339, 344, 339, 372, 373, - 374, 377, 112, 109, 339, 344, 339, 339, - 135, 339, 370, 339, 395, 339, 379, 379, - 339, 109, 339, 344, 339, 339, 339, 339, - 339, 370, 339, 370, 339, 339, 339, 379, - 379, 339, 109, 339, 344, 339, 339, 339, - 339, 339, 370, 339, 370, 339, 339, 339, - 379, 371, 339, 109, 339, 344, 339, 339, - 339, 339, 339, 370, 339, 364, 365, 369, - 369, 112, 109, 339, 344, 339, 364, 365, - 366, 369, 112, 109, 339, 344, 339, 339, - 137, 339, 362, 339, 396, 339, 379, 379, - 339, 109, 339, 344, 339, 339, 339, 339, - 339, 362, 339, 362, 339, 339, 339, 379, - 379, 339, 109, 339, 344, 339, 339, 339, - 339, 339, 362, 339, 362, 339, 339, 339, - 379, 363, 339, 109, 339, 344, 339, 339, - 339, 339, 339, 362, 339, 356, 357, 361, - 361, 112, 109, 339, 344, 339, 356, 357, - 358, 361, 112, 109, 339, 344, 339, 339, - 139, 339, 354, 339, 397, 339, 379, 379, - 339, 109, 339, 344, 339, 339, 339, 339, - 339, 354, 339, 354, 339, 339, 339, 379, - 379, 339, 109, 339, 344, 339, 339, 339, - 339, 339, 354, 339, 354, 339, 339, 339, - 379, 355, 339, 109, 339, 344, 339, 339, - 339, 339, 339, 354, 339, 348, 349, 353, - 353, 112, 109, 339, 344, 339, 348, 349, - 350, 353, 112, 109, 339, 344, 339, 339, - 141, 339, 346, 339, 398, 339, 379, 379, - 339, 109, 339, 344, 339, 339, 339, 339, - 339, 346, 339, 346, 339, 339, 339, 379, - 379, 339, 109, 339, 344, 339, 339, 339, - 339, 339, 346, 339, 346, 339, 339, 339, - 379, 347, 339, 109, 339, 344, 339, 339, - 339, 339, 339, 346, 339, 340, 341, 343, - 343, 112, 109, 339, 344, 339, 148, 149, - 150, 151, 399, 284, 76, 73, 282, 154, - 155, 155, 144, 282, 282, 148, 282, 161, - 400, 163, 164, 4, 1, 160, 165, 160, - 160, 35, 160, 168, 149, 150, 151, 401, - 402, 76, 403, 160, 404, 160, 155, 144, - 160, 160, 168, 160, 107, 405, 405, 76, - 403, 160, 165, 160, 160, 144, 160, 406, - 160, 160, 407, 160, 404, 160, 404, 160, - 408, 160, 205, 160, 406, 160, 160, 160, - 160, 404, 160, 168, 160, 220, 107, 405, - 405, 76, 403, 160, 165, 160, 160, 160, - 160, 160, 168, 160, 410, 409, 411, 411, - 409, 146, 409, 412, 409, 411, 411, 409, - 146, 409, 412, 409, 413, 409, 409, 414, - 409, 412, 409, 412, 409, 415, 409, 416, - 409, 413, 409, 409, 409, 409, 412, 409, - 148, 338, 338, 338, 338, 338, 338, 338, - 338, 338, 155, 338, 338, 338, 338, 148, - 338, 0 + 154, 155, 155, 144, 147, 156, 157, 158, + 159, 160, 147, 162, 163, 164, 165, 4, + 1, 161, 166, 161, 161, 35, 161, 161, + 161, 167, 161, 168, 163, 169, 169, 4, + 1, 161, 166, 161, 161, 161, 161, 161, + 161, 167, 161, 163, 169, 169, 4, 1, + 161, 166, 161, 161, 161, 161, 161, 161, + 167, 161, 170, 161, 161, 161, 17, 171, + 161, 1, 161, 166, 161, 161, 161, 161, + 161, 170, 161, 172, 173, 174, 175, 4, + 1, 161, 166, 161, 161, 33, 161, 161, + 161, 167, 161, 176, 173, 177, 177, 4, + 1, 161, 166, 161, 161, 161, 161, 161, + 161, 167, 161, 173, 177, 177, 4, 1, + 161, 166, 161, 161, 161, 161, 161, 161, + 167, 161, 178, 161, 161, 161, 17, 179, + 161, 1, 161, 166, 161, 161, 161, 161, + 161, 178, 161, 180, 181, 182, 183, 4, + 1, 161, 166, 161, 161, 31, 161, 161, + 161, 167, 161, 184, 181, 185, 185, 4, + 1, 161, 166, 161, 161, 161, 161, 161, + 161, 167, 161, 181, 185, 185, 4, 1, + 161, 166, 161, 161, 161, 161, 161, 161, + 167, 161, 186, 161, 161, 161, 17, 187, + 161, 1, 161, 166, 161, 161, 161, 161, + 161, 186, 161, 188, 189, 190, 191, 4, + 1, 161, 166, 161, 161, 29, 161, 161, + 161, 167, 161, 192, 189, 193, 193, 4, + 1, 161, 166, 161, 161, 161, 161, 161, + 161, 167, 161, 189, 193, 193, 4, 1, + 161, 166, 161, 161, 161, 161, 161, 161, + 167, 161, 194, 161, 161, 161, 17, 195, + 161, 1, 161, 166, 161, 161, 161, 161, + 161, 194, 161, 196, 197, 198, 199, 4, + 1, 161, 166, 161, 161, 27, 161, 161, + 161, 167, 161, 200, 197, 201, 201, 4, + 1, 161, 166, 161, 161, 161, 161, 161, + 161, 167, 161, 197, 201, 201, 4, 1, + 161, 166, 161, 161, 161, 161, 161, 161, + 167, 161, 17, 202, 161, 1, 161, 166, + 161, 203, 203, 161, 1, 161, 166, 161, + 204, 161, 161, 205, 161, 166, 161, 166, + 161, 206, 161, 207, 161, 204, 161, 161, + 161, 161, 166, 161, 17, 161, 203, 203, + 161, 1, 161, 166, 161, 203, 202, 161, + 1, 161, 166, 161, 208, 26, 209, 210, + 7, 1, 161, 166, 161, 26, 209, 210, + 7, 1, 161, 166, 161, 209, 209, 7, + 1, 161, 166, 161, 211, 23, 212, 213, + 10, 1, 161, 166, 161, 23, 212, 213, + 10, 1, 161, 166, 161, 212, 212, 10, + 1, 161, 166, 161, 214, 20, 215, 216, + 13, 1, 161, 166, 161, 20, 215, 216, + 13, 1, 161, 166, 161, 215, 215, 13, + 1, 161, 166, 161, 217, 17, 203, 218, + 161, 1, 161, 166, 161, 17, 203, 218, + 161, 1, 161, 166, 161, 197, 201, 201, + 4, 1, 161, 166, 161, 196, 197, 201, + 201, 4, 1, 161, 166, 161, 161, 161, + 161, 161, 161, 167, 161, 196, 197, 198, + 201, 4, 1, 161, 166, 161, 161, 27, + 161, 161, 161, 167, 161, 194, 161, 219, + 161, 203, 203, 161, 1, 161, 166, 161, + 161, 161, 161, 161, 194, 161, 194, 161, + 161, 161, 203, 203, 161, 1, 161, 166, + 161, 161, 161, 161, 161, 194, 161, 194, + 161, 161, 161, 203, 195, 161, 1, 161, + 166, 161, 161, 161, 161, 161, 194, 161, + 188, 189, 193, 193, 4, 1, 161, 166, + 161, 161, 161, 161, 161, 161, 167, 161, + 188, 189, 190, 193, 4, 1, 161, 166, + 161, 161, 29, 161, 161, 161, 167, 161, + 186, 161, 220, 161, 203, 203, 161, 1, + 161, 166, 161, 161, 161, 161, 161, 186, + 161, 186, 161, 161, 161, 203, 203, 161, + 1, 161, 166, 161, 161, 161, 161, 161, + 186, 161, 186, 161, 161, 161, 203, 187, + 161, 1, 161, 166, 161, 161, 161, 161, + 161, 186, 161, 180, 181, 185, 185, 4, + 1, 161, 166, 161, 161, 161, 161, 161, + 161, 167, 161, 180, 181, 182, 185, 4, + 1, 161, 166, 161, 161, 31, 161, 161, + 161, 167, 161, 178, 161, 221, 161, 203, + 203, 161, 1, 161, 166, 161, 161, 161, + 161, 161, 178, 161, 178, 161, 161, 161, + 203, 203, 161, 1, 161, 166, 161, 161, + 161, 161, 161, 178, 161, 178, 161, 161, + 161, 203, 179, 161, 1, 161, 166, 161, + 161, 161, 161, 161, 178, 161, 172, 173, + 177, 177, 4, 1, 161, 166, 161, 161, + 161, 161, 161, 161, 167, 161, 172, 173, + 174, 177, 4, 1, 161, 166, 161, 161, + 33, 161, 161, 161, 167, 161, 170, 161, + 222, 161, 203, 203, 161, 1, 161, 166, + 161, 161, 161, 161, 161, 170, 161, 170, + 161, 161, 161, 203, 203, 161, 1, 161, + 166, 161, 161, 161, 161, 161, 170, 161, + 170, 161, 161, 161, 203, 171, 161, 1, + 161, 166, 161, 161, 161, 161, 161, 170, + 161, 162, 163, 169, 169, 4, 1, 161, + 166, 161, 161, 161, 161, 161, 161, 167, + 161, 162, 163, 164, 169, 4, 1, 161, + 166, 161, 161, 35, 161, 161, 161, 167, + 161, 224, 225, 226, 227, 40, 37, 223, + 228, 223, 223, 71, 223, 223, 223, 229, + 223, 230, 225, 231, 227, 40, 37, 223, + 228, 223, 223, 223, 223, 223, 223, 229, + 223, 225, 231, 227, 40, 37, 223, 228, + 223, 223, 223, 223, 223, 223, 229, 223, + 232, 223, 223, 223, 53, 233, 223, 37, + 223, 228, 223, 223, 223, 223, 223, 232, + 223, 234, 235, 236, 237, 40, 37, 223, + 228, 223, 223, 69, 223, 223, 223, 229, + 223, 238, 235, 239, 239, 40, 37, 223, + 228, 223, 223, 223, 223, 223, 223, 229, + 223, 235, 239, 239, 40, 37, 223, 228, + 223, 223, 223, 223, 223, 223, 229, 223, + 240, 223, 223, 223, 53, 241, 223, 37, + 223, 228, 223, 223, 223, 223, 223, 240, + 223, 242, 243, 244, 245, 40, 37, 223, + 228, 223, 223, 67, 223, 223, 223, 229, + 223, 246, 243, 247, 247, 40, 37, 223, + 228, 223, 223, 223, 223, 223, 223, 229, + 223, 243, 247, 247, 40, 37, 223, 228, + 223, 223, 223, 223, 223, 223, 229, 223, + 248, 223, 223, 223, 53, 249, 223, 37, + 223, 228, 223, 223, 223, 223, 223, 248, + 223, 250, 251, 252, 253, 40, 37, 223, + 228, 223, 223, 65, 223, 223, 223, 229, + 223, 254, 251, 255, 255, 40, 37, 223, + 228, 223, 223, 223, 223, 223, 223, 229, + 223, 251, 255, 255, 40, 37, 223, 228, + 223, 223, 223, 223, 223, 223, 229, 223, + 256, 223, 223, 223, 53, 257, 223, 37, + 223, 228, 223, 223, 223, 223, 223, 256, + 223, 258, 259, 260, 261, 40, 37, 223, + 228, 223, 223, 63, 223, 223, 223, 229, + 223, 262, 259, 263, 263, 40, 37, 223, + 228, 223, 223, 223, 223, 223, 223, 229, + 223, 259, 263, 263, 40, 37, 223, 228, + 223, 223, 223, 223, 223, 223, 229, 223, + 53, 264, 223, 37, 223, 228, 223, 265, + 265, 223, 37, 223, 228, 223, 266, 223, + 223, 267, 223, 228, 223, 228, 223, 268, + 223, 269, 223, 266, 223, 223, 223, 223, + 228, 223, 53, 223, 265, 265, 223, 37, + 223, 228, 223, 265, 264, 223, 37, 223, + 228, 223, 270, 62, 271, 272, 43, 37, + 223, 228, 223, 62, 271, 272, 43, 37, + 223, 228, 223, 271, 271, 43, 37, 223, + 228, 223, 273, 59, 274, 275, 46, 37, + 223, 228, 223, 59, 274, 275, 46, 37, + 223, 228, 223, 274, 274, 46, 37, 223, + 228, 223, 276, 56, 277, 278, 49, 37, + 223, 228, 223, 56, 277, 278, 49, 37, + 223, 228, 223, 277, 277, 49, 37, 223, + 228, 223, 279, 53, 265, 280, 223, 37, + 223, 228, 223, 53, 265, 280, 223, 37, + 223, 228, 223, 259, 263, 263, 40, 37, + 223, 228, 223, 258, 259, 263, 263, 40, + 37, 223, 228, 223, 223, 223, 223, 223, + 223, 229, 223, 258, 259, 260, 263, 40, + 37, 223, 228, 223, 223, 63, 223, 223, + 223, 229, 223, 256, 223, 281, 223, 265, + 265, 223, 37, 223, 228, 223, 223, 223, + 223, 223, 256, 223, 256, 223, 223, 223, + 265, 265, 223, 37, 223, 228, 223, 223, + 223, 223, 223, 256, 223, 256, 223, 223, + 223, 265, 257, 223, 37, 223, 228, 223, + 223, 223, 223, 223, 256, 223, 250, 251, + 255, 255, 40, 37, 223, 228, 223, 223, + 223, 223, 223, 223, 229, 223, 250, 251, + 252, 255, 40, 37, 223, 228, 223, 223, + 65, 223, 223, 223, 229, 223, 248, 223, + 282, 223, 265, 265, 223, 37, 223, 228, + 223, 223, 223, 223, 223, 248, 223, 248, + 223, 223, 223, 265, 265, 223, 37, 223, + 228, 223, 223, 223, 223, 223, 248, 223, + 248, 223, 223, 223, 265, 249, 223, 37, + 223, 228, 223, 223, 223, 223, 223, 248, + 223, 242, 243, 247, 247, 40, 37, 223, + 228, 223, 223, 223, 223, 223, 223, 229, + 223, 242, 243, 244, 247, 40, 37, 223, + 228, 223, 223, 67, 223, 223, 223, 229, + 223, 240, 223, 283, 223, 265, 265, 223, + 37, 223, 228, 223, 223, 223, 223, 223, + 240, 223, 240, 223, 223, 223, 265, 265, + 223, 37, 223, 228, 223, 223, 223, 223, + 223, 240, 223, 240, 223, 223, 223, 265, + 241, 223, 37, 223, 228, 223, 223, 223, + 223, 223, 240, 223, 234, 235, 239, 239, + 40, 37, 223, 228, 223, 223, 223, 223, + 223, 223, 229, 223, 234, 235, 236, 239, + 40, 37, 223, 228, 223, 223, 69, 223, + 223, 223, 229, 223, 232, 223, 284, 223, + 265, 265, 223, 37, 223, 228, 223, 223, + 223, 223, 223, 232, 223, 232, 223, 223, + 223, 265, 265, 223, 37, 223, 228, 223, + 223, 223, 223, 223, 232, 223, 232, 223, + 223, 223, 265, 233, 223, 37, 223, 228, + 223, 223, 223, 223, 223, 232, 223, 70, + 39, 39, 40, 37, 223, 224, 225, 231, + 227, 40, 37, 223, 228, 223, 223, 223, + 223, 223, 223, 229, 223, 286, 151, 287, + 287, 76, 73, 285, 154, 285, 285, 285, + 285, 285, 285, 158, 285, 151, 287, 287, + 76, 73, 285, 154, 285, 285, 285, 285, + 285, 285, 158, 285, 288, 285, 285, 285, + 90, 289, 285, 73, 285, 154, 285, 285, + 285, 285, 285, 288, 285, 290, 291, 292, + 293, 76, 73, 285, 154, 285, 285, 106, + 285, 285, 285, 158, 285, 294, 291, 295, + 295, 76, 73, 285, 154, 285, 285, 285, + 285, 285, 285, 158, 285, 291, 295, 295, + 76, 73, 285, 154, 285, 285, 285, 285, + 285, 285, 158, 285, 296, 285, 285, 285, + 90, 297, 285, 73, 285, 154, 285, 285, + 285, 285, 285, 296, 285, 298, 299, 300, + 301, 76, 73, 285, 154, 285, 285, 104, + 285, 285, 285, 158, 285, 302, 299, 303, + 303, 76, 73, 285, 154, 285, 285, 285, + 285, 285, 285, 158, 285, 299, 303, 303, + 76, 73, 285, 154, 285, 285, 285, 285, + 285, 285, 158, 285, 304, 285, 285, 285, + 90, 305, 285, 73, 285, 154, 285, 285, + 285, 285, 285, 304, 285, 306, 307, 308, + 309, 76, 73, 285, 154, 285, 285, 102, + 285, 285, 285, 158, 285, 310, 307, 311, + 311, 76, 73, 285, 154, 285, 285, 285, + 285, 285, 285, 158, 285, 307, 311, 311, + 76, 73, 285, 154, 285, 285, 285, 285, + 285, 285, 158, 285, 312, 285, 285, 285, + 90, 313, 285, 73, 285, 154, 285, 285, + 285, 285, 285, 312, 285, 314, 315, 316, + 317, 76, 73, 285, 154, 285, 285, 100, + 285, 285, 285, 158, 285, 318, 315, 319, + 319, 76, 73, 285, 154, 285, 285, 285, + 285, 285, 285, 158, 285, 315, 319, 319, + 76, 73, 285, 154, 285, 285, 285, 285, + 285, 285, 158, 285, 90, 320, 285, 73, + 285, 154, 285, 321, 321, 285, 73, 285, + 154, 285, 322, 285, 285, 323, 285, 154, + 285, 154, 285, 324, 285, 325, 285, 322, + 285, 285, 285, 285, 154, 285, 90, 285, + 321, 321, 285, 73, 285, 154, 285, 321, + 320, 285, 73, 285, 154, 285, 326, 99, + 327, 328, 80, 73, 285, 154, 285, 99, + 327, 328, 80, 73, 285, 154, 285, 327, + 327, 80, 73, 285, 154, 285, 329, 96, + 330, 331, 83, 73, 285, 154, 285, 96, + 330, 331, 83, 73, 285, 154, 285, 330, + 330, 83, 73, 285, 154, 285, 332, 93, + 333, 334, 86, 73, 285, 154, 285, 93, + 333, 334, 86, 73, 285, 154, 285, 333, + 333, 86, 73, 285, 154, 285, 335, 90, + 321, 336, 285, 73, 285, 154, 285, 90, + 321, 336, 285, 73, 285, 154, 285, 315, + 319, 319, 76, 73, 285, 154, 285, 314, + 315, 319, 319, 76, 73, 285, 154, 285, + 285, 285, 285, 285, 285, 158, 285, 314, + 315, 316, 319, 76, 73, 285, 154, 285, + 285, 100, 285, 285, 285, 158, 285, 312, + 285, 337, 285, 321, 321, 285, 73, 285, + 154, 285, 285, 285, 285, 285, 312, 285, + 312, 285, 285, 285, 321, 321, 285, 73, + 285, 154, 285, 285, 285, 285, 285, 312, + 285, 312, 285, 285, 285, 321, 313, 285, + 73, 285, 154, 285, 285, 285, 285, 285, + 312, 285, 306, 307, 311, 311, 76, 73, + 285, 154, 285, 285, 285, 285, 285, 285, + 158, 285, 306, 307, 308, 311, 76, 73, + 285, 154, 285, 285, 102, 285, 285, 285, + 158, 285, 304, 285, 338, 285, 321, 321, + 285, 73, 285, 154, 285, 285, 285, 285, + 285, 304, 285, 304, 285, 285, 285, 321, + 321, 285, 73, 285, 154, 285, 285, 285, + 285, 285, 304, 285, 304, 285, 285, 285, + 321, 305, 285, 73, 285, 154, 285, 285, + 285, 285, 285, 304, 285, 298, 299, 303, + 303, 76, 73, 285, 154, 285, 285, 285, + 285, 285, 285, 158, 285, 298, 299, 300, + 303, 76, 73, 285, 154, 285, 285, 104, + 285, 285, 285, 158, 285, 296, 285, 339, + 285, 321, 321, 285, 73, 285, 154, 285, + 285, 285, 285, 285, 296, 285, 296, 285, + 285, 285, 321, 321, 285, 73, 285, 154, + 285, 285, 285, 285, 285, 296, 285, 296, + 285, 285, 285, 321, 297, 285, 73, 285, + 154, 285, 285, 285, 285, 285, 296, 285, + 290, 291, 295, 295, 76, 73, 285, 154, + 285, 285, 285, 285, 285, 285, 158, 285, + 290, 291, 292, 295, 76, 73, 285, 154, + 285, 285, 106, 285, 285, 285, 158, 285, + 288, 285, 340, 285, 321, 321, 285, 73, + 285, 154, 285, 285, 285, 285, 285, 288, + 285, 288, 285, 285, 285, 321, 321, 285, + 73, 285, 154, 285, 285, 285, 285, 285, + 288, 285, 288, 285, 285, 285, 321, 289, + 285, 73, 285, 154, 285, 285, 285, 285, + 285, 288, 285, 107, 75, 75, 76, 73, + 341, 341, 341, 341, 144, 341, 150, 151, + 287, 287, 76, 73, 285, 154, 285, 285, + 285, 285, 285, 285, 158, 285, 107, 75, + 75, 76, 73, 341, 343, 344, 345, 346, + 112, 109, 342, 347, 342, 342, 143, 342, + 342, 342, 348, 342, 349, 344, 346, 346, + 112, 109, 342, 347, 342, 342, 342, 342, + 342, 342, 348, 342, 344, 346, 346, 112, + 109, 342, 347, 342, 342, 342, 342, 342, + 342, 348, 342, 350, 342, 342, 342, 125, + 351, 342, 109, 342, 347, 342, 342, 342, + 342, 342, 350, 342, 352, 353, 354, 355, + 112, 109, 342, 347, 342, 342, 141, 342, + 342, 342, 348, 342, 356, 353, 357, 357, + 112, 109, 342, 347, 342, 342, 342, 342, + 342, 342, 348, 342, 353, 357, 357, 112, + 109, 342, 347, 342, 342, 342, 342, 342, + 342, 348, 342, 358, 342, 342, 342, 125, + 359, 342, 109, 342, 347, 342, 342, 342, + 342, 342, 358, 342, 360, 361, 362, 363, + 112, 109, 342, 347, 342, 342, 139, 342, + 342, 342, 348, 342, 364, 361, 365, 365, + 112, 109, 342, 347, 342, 342, 342, 342, + 342, 342, 348, 342, 361, 365, 365, 112, + 109, 342, 347, 342, 342, 342, 342, 342, + 342, 348, 342, 366, 342, 342, 342, 125, + 367, 342, 109, 342, 347, 342, 342, 342, + 342, 342, 366, 342, 368, 369, 370, 371, + 112, 109, 342, 347, 342, 342, 137, 342, + 342, 342, 348, 342, 372, 369, 373, 373, + 112, 109, 342, 347, 342, 342, 342, 342, + 342, 342, 348, 342, 369, 373, 373, 112, + 109, 342, 347, 342, 342, 342, 342, 342, + 342, 348, 342, 374, 342, 342, 342, 125, + 375, 342, 109, 342, 347, 342, 342, 342, + 342, 342, 374, 342, 376, 377, 378, 379, + 112, 109, 342, 347, 342, 342, 135, 342, + 342, 342, 348, 342, 380, 377, 381, 381, + 112, 109, 342, 347, 342, 342, 342, 342, + 342, 342, 348, 342, 377, 381, 381, 112, + 109, 342, 347, 342, 342, 342, 342, 342, + 342, 348, 342, 125, 382, 342, 109, 342, + 347, 342, 383, 383, 342, 109, 342, 347, + 342, 384, 342, 342, 385, 342, 347, 342, + 347, 342, 386, 342, 387, 342, 384, 342, + 342, 342, 342, 347, 342, 125, 342, 383, + 383, 342, 109, 342, 347, 342, 383, 382, + 342, 109, 342, 347, 342, 388, 134, 389, + 390, 115, 109, 342, 347, 342, 134, 389, + 390, 115, 109, 342, 347, 342, 389, 389, + 115, 109, 342, 347, 342, 391, 131, 392, + 393, 118, 109, 342, 347, 342, 131, 392, + 393, 118, 109, 342, 347, 342, 392, 392, + 118, 109, 342, 347, 342, 394, 128, 395, + 396, 121, 109, 342, 347, 342, 128, 395, + 396, 121, 109, 342, 347, 342, 395, 395, + 121, 109, 342, 347, 342, 397, 125, 383, + 398, 342, 109, 342, 347, 342, 125, 383, + 398, 342, 109, 342, 347, 342, 377, 381, + 381, 112, 109, 342, 347, 342, 376, 377, + 381, 381, 112, 109, 342, 347, 342, 342, + 342, 342, 342, 342, 348, 342, 376, 377, + 378, 381, 112, 109, 342, 347, 342, 342, + 135, 342, 342, 342, 348, 342, 374, 342, + 399, 342, 383, 383, 342, 109, 342, 347, + 342, 342, 342, 342, 342, 374, 342, 374, + 342, 342, 342, 383, 383, 342, 109, 342, + 347, 342, 342, 342, 342, 342, 374, 342, + 374, 342, 342, 342, 383, 375, 342, 109, + 342, 347, 342, 342, 342, 342, 342, 374, + 342, 368, 369, 373, 373, 112, 109, 342, + 347, 342, 342, 342, 342, 342, 342, 348, + 342, 368, 369, 370, 373, 112, 109, 342, + 347, 342, 342, 137, 342, 342, 342, 348, + 342, 366, 342, 400, 342, 383, 383, 342, + 109, 342, 347, 342, 342, 342, 342, 342, + 366, 342, 366, 342, 342, 342, 383, 383, + 342, 109, 342, 347, 342, 342, 342, 342, + 342, 366, 342, 366, 342, 342, 342, 383, + 367, 342, 109, 342, 347, 342, 342, 342, + 342, 342, 366, 342, 360, 361, 365, 365, + 112, 109, 342, 347, 342, 342, 342, 342, + 342, 342, 348, 342, 360, 361, 362, 365, + 112, 109, 342, 347, 342, 342, 139, 342, + 342, 342, 348, 342, 358, 342, 401, 342, + 383, 383, 342, 109, 342, 347, 342, 342, + 342, 342, 342, 358, 342, 358, 342, 342, + 342, 383, 383, 342, 109, 342, 347, 342, + 342, 342, 342, 342, 358, 342, 358, 342, + 342, 342, 383, 359, 342, 109, 342, 347, + 342, 342, 342, 342, 342, 358, 342, 352, + 353, 357, 357, 112, 109, 342, 347, 342, + 342, 342, 342, 342, 342, 348, 342, 352, + 353, 354, 357, 112, 109, 342, 347, 342, + 342, 141, 342, 342, 342, 348, 342, 350, + 342, 402, 342, 383, 383, 342, 109, 342, + 347, 342, 342, 342, 342, 342, 350, 342, + 350, 342, 342, 342, 383, 383, 342, 109, + 342, 347, 342, 342, 342, 342, 342, 350, + 342, 350, 342, 342, 342, 383, 351, 342, + 109, 342, 347, 342, 342, 342, 342, 342, + 350, 342, 343, 344, 346, 346, 112, 109, + 342, 347, 342, 342, 342, 342, 342, 342, + 348, 342, 148, 149, 150, 151, 403, 287, + 76, 73, 285, 154, 155, 155, 144, 285, + 285, 148, 158, 285, 162, 404, 164, 165, + 4, 1, 161, 166, 161, 161, 35, 161, + 161, 161, 167, 161, 170, 149, 150, 151, + 405, 406, 76, 407, 161, 408, 161, 155, + 144, 161, 161, 170, 158, 161, 107, 409, + 409, 76, 407, 161, 166, 161, 161, 144, + 161, 410, 161, 161, 411, 161, 408, 161, + 408, 161, 412, 161, 207, 161, 410, 161, + 161, 161, 161, 408, 161, 170, 161, 222, + 107, 409, 409, 76, 407, 161, 166, 161, + 161, 161, 161, 161, 170, 161, 414, 413, + 415, 415, 413, 146, 413, 416, 413, 415, + 415, 413, 146, 413, 416, 413, 417, 413, + 413, 418, 413, 416, 413, 416, 413, 419, + 413, 420, 413, 417, 413, 413, 413, 413, + 416, 413, 148, 341, 341, 341, 341, 341, + 341, 341, 341, 341, 155, 341, 341, 341, + 341, 148, 341, 0 }; static const short _indic_syllable_machine_trans_targs[] = { 138, 160, 166, 2, 167, 3, 5, 170, 6, 8, 173, 9, 11, 176, 12, 14, 15, 159, 17, 18, 175, 20, 21, 172, - 23, 24, 169, 178, 182, 183, 187, 188, - 192, 193, 197, 198, 138, 221, 227, 36, - 228, 37, 39, 231, 40, 42, 234, 43, - 45, 237, 46, 48, 49, 220, 51, 52, - 236, 54, 55, 233, 57, 58, 230, 239, - 243, 244, 248, 249, 253, 254, 258, 260, - 138, 281, 287, 70, 288, 138, 71, 73, - 291, 74, 76, 294, 77, 79, 297, 80, - 82, 83, 280, 85, 86, 296, 88, 89, - 293, 91, 92, 290, 299, 303, 304, 308, - 309, 313, 314, 318, 138, 343, 349, 103, - 350, 104, 106, 353, 107, 109, 356, 110, - 112, 359, 113, 115, 116, 342, 118, 119, - 358, 121, 122, 355, 124, 125, 352, 361, - 365, 366, 370, 371, 375, 376, 380, 381, - 320, 138, 394, 138, 139, 200, 261, 263, - 319, 321, 283, 322, 382, 383, 392, 399, - 138, 140, 142, 33, 199, 162, 141, 32, - 143, 195, 144, 146, 31, 194, 145, 30, - 147, 190, 148, 150, 29, 189, 149, 28, - 151, 185, 152, 154, 27, 184, 153, 26, - 155, 180, 156, 158, 25, 179, 157, 1, - 165, 0, 161, 164, 163, 138, 168, 4, - 22, 171, 7, 19, 174, 10, 16, 177, - 13, 181, 186, 191, 196, 138, 201, 203, - 67, 259, 223, 202, 66, 204, 256, 205, - 207, 65, 255, 206, 64, 208, 251, 209, - 211, 63, 250, 210, 62, 212, 246, 213, - 215, 61, 245, 214, 60, 216, 241, 217, - 219, 59, 240, 218, 35, 226, 34, 222, - 225, 224, 138, 229, 38, 56, 232, 41, - 53, 235, 44, 50, 238, 47, 242, 247, - 252, 257, 138, 262, 100, 264, 316, 265, - 267, 99, 315, 266, 98, 268, 311, 269, - 271, 97, 310, 270, 96, 272, 306, 273, - 275, 95, 305, 274, 94, 276, 301, 277, - 279, 93, 300, 278, 69, 286, 68, 282, - 285, 284, 138, 289, 72, 90, 292, 75, - 87, 295, 78, 84, 298, 81, 302, 307, - 312, 317, 138, 138, 323, 325, 134, 133, - 345, 324, 326, 378, 327, 329, 132, 377, - 328, 131, 330, 373, 331, 333, 130, 372, - 332, 129, 334, 368, 335, 337, 128, 367, - 336, 127, 338, 363, 339, 341, 126, 362, - 340, 102, 348, 101, 344, 347, 346, 138, - 351, 105, 123, 354, 108, 120, 357, 111, - 117, 360, 114, 364, 369, 374, 379, 135, - 384, 385, 391, 386, 388, 136, 387, 390, - 389, 138, 393, 137, 396, 395, 398, 397, - 138 + 23, 24, 169, 179, 183, 184, 188, 189, + 193, 194, 198, 199, 138, 222, 228, 36, + 229, 37, 39, 232, 40, 42, 235, 43, + 45, 238, 46, 48, 49, 221, 51, 52, + 237, 54, 55, 234, 57, 58, 231, 241, + 245, 246, 250, 251, 255, 256, 260, 262, + 138, 283, 289, 70, 290, 138, 71, 73, + 293, 74, 76, 296, 77, 79, 299, 80, + 82, 83, 282, 85, 86, 298, 88, 89, + 295, 91, 92, 292, 302, 306, 307, 311, + 312, 316, 317, 321, 138, 346, 352, 103, + 353, 104, 106, 356, 107, 109, 359, 110, + 112, 362, 113, 115, 116, 345, 118, 119, + 361, 121, 122, 358, 124, 125, 355, 365, + 369, 370, 374, 375, 379, 380, 384, 385, + 323, 138, 398, 138, 139, 201, 263, 265, + 322, 324, 285, 325, 386, 387, 301, 396, + 403, 138, 140, 142, 33, 200, 162, 178, + 141, 32, 143, 196, 144, 146, 31, 195, + 145, 30, 147, 191, 148, 150, 29, 190, + 149, 28, 151, 186, 152, 154, 27, 185, + 153, 26, 155, 181, 156, 158, 25, 180, + 157, 1, 165, 0, 161, 164, 163, 138, + 168, 4, 22, 171, 7, 19, 174, 10, + 16, 177, 13, 182, 187, 192, 197, 138, + 202, 204, 67, 261, 224, 240, 203, 66, + 205, 258, 206, 208, 65, 257, 207, 64, + 209, 253, 210, 212, 63, 252, 211, 62, + 213, 248, 214, 216, 61, 247, 215, 60, + 217, 243, 218, 220, 59, 242, 219, 35, + 227, 34, 223, 226, 225, 138, 230, 38, + 56, 233, 41, 53, 236, 44, 50, 239, + 47, 244, 249, 254, 259, 138, 264, 100, + 266, 319, 267, 269, 99, 318, 268, 98, + 270, 314, 271, 273, 97, 313, 272, 96, + 274, 309, 275, 277, 95, 308, 276, 94, + 278, 304, 279, 281, 93, 303, 280, 69, + 288, 68, 284, 287, 286, 138, 291, 72, + 90, 294, 75, 87, 297, 78, 84, 300, + 81, 305, 310, 315, 320, 138, 138, 326, + 328, 134, 133, 348, 364, 327, 329, 382, + 330, 332, 132, 381, 331, 131, 333, 377, + 334, 336, 130, 376, 335, 129, 337, 372, + 338, 340, 128, 371, 339, 127, 341, 367, + 342, 344, 126, 366, 343, 102, 351, 101, + 347, 350, 349, 138, 354, 105, 123, 357, + 108, 120, 360, 111, 117, 363, 114, 368, + 373, 378, 383, 135, 388, 389, 395, 390, + 392, 136, 391, 394, 393, 138, 397, 137, + 400, 399, 402, 401, 138 }; static const char _indic_syllable_machine_trans_actions[] = { @@ -705,40 +782,40 @@ static const char _indic_syllable_machine_trans_actions[] = { 2, 0, 0, 2, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 6, 8, 0, 11, 2, 2, 6, 0, - 12, 12, 0, 2, 6, 2, 2, 0, - 13, 2, 0, 0, 2, 0, 2, 0, - 2, 2, 2, 0, 0, 2, 2, 0, - 2, 2, 2, 0, 0, 2, 2, 0, - 2, 2, 2, 0, 0, 2, 2, 0, - 2, 2, 2, 0, 0, 2, 2, 0, - 2, 0, 0, 0, 0, 14, 2, 0, - 0, 2, 0, 0, 2, 0, 0, 2, - 0, 2, 2, 2, 2, 15, 2, 0, - 0, 2, 0, 2, 0, 2, 2, 2, - 0, 0, 2, 2, 0, 2, 2, 2, - 0, 0, 2, 2, 0, 2, 2, 2, - 0, 0, 2, 2, 0, 2, 2, 2, - 0, 0, 2, 2, 0, 2, 0, 0, - 0, 0, 16, 2, 0, 0, 2, 0, - 0, 2, 0, 0, 2, 0, 2, 2, - 2, 2, 17, 6, 0, 6, 2, 6, - 0, 0, 6, 6, 0, 6, 2, 6, - 0, 0, 6, 6, 0, 6, 2, 6, - 0, 0, 6, 6, 0, 6, 2, 6, - 0, 0, 6, 6, 0, 2, 0, 0, - 0, 0, 18, 2, 0, 0, 2, 0, - 0, 2, 0, 0, 2, 0, 2, 2, - 2, 2, 19, 20, 2, 0, 0, 0, - 0, 2, 2, 2, 2, 0, 0, 2, + 12, 12, 0, 2, 6, 2, 6, 2, + 0, 13, 2, 0, 0, 2, 0, 2, 2, 0, 2, 2, 2, 0, 0, 2, 2, 0, 2, 2, 2, 0, 0, 2, 2, 0, 2, 2, 2, 0, 0, 2, - 2, 0, 2, 0, 0, 0, 0, 21, + 2, 0, 2, 2, 2, 0, 0, 2, + 2, 0, 2, 0, 0, 0, 0, 14, 2, 0, 0, 2, 0, 0, 2, 0, - 0, 2, 0, 2, 2, 2, 2, 0, - 0, 22, 22, 0, 0, 0, 0, 0, - 0, 23, 2, 0, 0, 0, 0, 0, - 24 + 0, 2, 0, 2, 2, 2, 2, 15, + 2, 0, 0, 2, 0, 2, 2, 0, + 2, 2, 2, 0, 0, 2, 2, 0, + 2, 2, 2, 0, 0, 2, 2, 0, + 2, 2, 2, 0, 0, 2, 2, 0, + 2, 2, 2, 0, 0, 2, 2, 0, + 2, 0, 0, 0, 0, 16, 2, 0, + 0, 2, 0, 0, 2, 0, 0, 2, + 0, 2, 2, 2, 2, 17, 6, 0, + 6, 2, 6, 0, 0, 6, 6, 0, + 6, 2, 6, 0, 0, 6, 6, 0, + 6, 2, 6, 0, 0, 6, 6, 0, + 6, 2, 6, 0, 0, 6, 6, 0, + 2, 0, 0, 0, 0, 18, 2, 0, + 0, 2, 0, 0, 2, 0, 0, 2, + 0, 2, 2, 2, 2, 19, 20, 2, + 0, 0, 0, 0, 2, 2, 2, 2, + 2, 0, 0, 2, 2, 0, 2, 2, + 2, 0, 0, 2, 2, 0, 2, 2, + 2, 0, 0, 2, 2, 0, 2, 2, + 2, 0, 0, 2, 2, 0, 2, 0, + 0, 0, 0, 21, 2, 0, 0, 2, + 0, 0, 2, 0, 0, 2, 0, 2, + 2, 2, 2, 0, 0, 22, 22, 0, + 0, 0, 0, 0, 0, 23, 2, 0, + 0, 0, 0, 0, 24 }; static const char _indic_syllable_machine_to_state_actions[] = { @@ -791,7 +868,8 @@ static const char _indic_syllable_machine_to_state_actions[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0 + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0 }; static const char _indic_syllable_machine_from_state_actions[] = { @@ -844,7 +922,8 @@ static const char _indic_syllable_machine_from_state_actions[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0 + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0 }; static const short _indic_syllable_machine_eof_trans[] = { @@ -865,39 +944,40 @@ static const short _indic_syllable_machine_eof_trans[] = { 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 109, 73, - 1, 146, 0, 161, 161, 161, 161, 161, - 161, 161, 161, 161, 161, 161, 161, 161, - 161, 161, 161, 161, 161, 161, 161, 161, - 161, 161, 161, 161, 161, 161, 161, 161, - 161, 161, 161, 161, 161, 161, 161, 161, - 161, 161, 161, 161, 161, 161, 161, 161, - 161, 161, 161, 161, 161, 161, 161, 161, - 161, 161, 161, 161, 161, 161, 161, 161, - 222, 222, 222, 222, 222, 222, 222, 222, - 222, 222, 222, 222, 222, 222, 222, 222, - 222, 222, 222, 222, 222, 222, 222, 222, - 222, 222, 222, 222, 222, 222, 222, 222, - 222, 222, 222, 222, 222, 222, 222, 222, - 222, 222, 222, 222, 222, 222, 222, 222, - 222, 222, 222, 222, 222, 222, 222, 222, - 222, 222, 222, 222, 222, 283, 283, 283, - 283, 283, 283, 283, 283, 283, 283, 283, - 283, 283, 283, 283, 283, 283, 283, 283, - 283, 283, 283, 283, 283, 283, 283, 283, - 283, 283, 283, 283, 283, 283, 283, 283, - 283, 283, 283, 283, 283, 283, 283, 283, - 283, 283, 283, 283, 283, 283, 283, 283, - 283, 283, 283, 283, 283, 283, 283, 339, - 283, 339, 340, 340, 340, 340, 340, 340, - 340, 340, 340, 340, 340, 340, 340, 340, - 340, 340, 340, 340, 340, 340, 340, 340, - 340, 340, 340, 340, 340, 340, 340, 340, - 340, 340, 340, 340, 340, 340, 340, 340, - 340, 340, 340, 340, 340, 340, 340, 340, - 340, 340, 340, 340, 340, 340, 340, 340, - 340, 340, 340, 340, 340, 340, 283, 161, - 161, 161, 161, 161, 161, 161, 161, 161, - 410, 410, 410, 410, 410, 410, 410, 339 + 1, 146, 0, 162, 162, 162, 162, 162, + 162, 162, 162, 162, 162, 162, 162, 162, + 162, 162, 162, 162, 162, 162, 162, 162, + 162, 162, 162, 162, 162, 162, 162, 162, + 162, 162, 162, 162, 162, 162, 162, 162, + 162, 162, 162, 162, 162, 162, 162, 162, + 162, 162, 162, 162, 162, 162, 162, 162, + 162, 162, 162, 162, 162, 162, 162, 162, + 162, 224, 224, 224, 224, 224, 224, 224, + 224, 224, 224, 224, 224, 224, 224, 224, + 224, 224, 224, 224, 224, 224, 224, 224, + 224, 224, 224, 224, 224, 224, 224, 224, + 224, 224, 224, 224, 224, 224, 224, 224, + 224, 224, 224, 224, 224, 224, 224, 224, + 224, 224, 224, 224, 224, 224, 224, 224, + 224, 224, 224, 224, 224, 224, 224, 286, + 286, 286, 286, 286, 286, 286, 286, 286, + 286, 286, 286, 286, 286, 286, 286, 286, + 286, 286, 286, 286, 286, 286, 286, 286, + 286, 286, 286, 286, 286, 286, 286, 286, + 286, 286, 286, 286, 286, 286, 286, 286, + 286, 286, 286, 286, 286, 286, 286, 286, + 286, 286, 286, 286, 286, 286, 286, 286, + 286, 286, 342, 286, 342, 343, 343, 343, + 343, 343, 343, 343, 343, 343, 343, 343, + 343, 343, 343, 343, 343, 343, 343, 343, + 343, 343, 343, 343, 343, 343, 343, 343, + 343, 343, 343, 343, 343, 343, 343, 343, + 343, 343, 343, 343, 343, 343, 343, 343, + 343, 343, 343, 343, 343, 343, 343, 343, + 343, 343, 343, 343, 343, 343, 343, 343, + 343, 343, 286, 162, 162, 162, 162, 162, + 162, 162, 162, 162, 414, 414, 414, 414, + 414, 414, 414, 342 }; static const int indic_syllable_machine_start = 138; @@ -911,7 +991,7 @@ static const int indic_syllable_machine_en_main = 138; -#line 92 "hb-ot-shape-complex-indic-machine.rl" +#line 93 "hb-ot-shape-complex-indic-machine.rl" #define found_syllable(syllable_type) \ @@ -930,7 +1010,7 @@ find_syllables (hb_buffer_t *buffer) int cs; hb_glyph_info_t *info = buffer->info; -#line 934 "hb-ot-shape-complex-indic-machine.hh" +#line 1014 "hb-ot-shape-complex-indic-machine.hh" { cs = indic_syllable_machine_start; ts = 0; @@ -938,7 +1018,7 @@ find_syllables (hb_buffer_t *buffer) act = 0; } -#line 112 "hb-ot-shape-complex-indic-machine.rl" +#line 113 "hb-ot-shape-complex-indic-machine.rl" p = 0; @@ -946,7 +1026,7 @@ find_syllables (hb_buffer_t *buffer) unsigned int syllable_serial = 1; -#line 950 "hb-ot-shape-complex-indic-machine.hh" +#line 1030 "hb-ot-shape-complex-indic-machine.hh" { int _slen; int _trans; @@ -960,7 +1040,7 @@ _resume: #line 1 "NONE" {ts = p;} break; -#line 964 "hb-ot-shape-complex-indic-machine.hh" +#line 1044 "hb-ot-shape-complex-indic-machine.hh" } _keys = _indic_syllable_machine_trans_keys + (cs<<1); @@ -983,71 +1063,71 @@ _eof_trans: {te = p+1;} break; case 14: -#line 83 "hb-ot-shape-complex-indic-machine.rl" +#line 84 "hb-ot-shape-complex-indic-machine.rl" {te = p+1;{ found_syllable (consonant_syllable); }} break; case 16: -#line 84 "hb-ot-shape-complex-indic-machine.rl" +#line 85 "hb-ot-shape-complex-indic-machine.rl" {te = p+1;{ found_syllable (vowel_syllable); }} break; case 21: -#line 85 "hb-ot-shape-complex-indic-machine.rl" +#line 86 "hb-ot-shape-complex-indic-machine.rl" {te = p+1;{ found_syllable (standalone_cluster); }} break; case 24: -#line 86 "hb-ot-shape-complex-indic-machine.rl" +#line 87 "hb-ot-shape-complex-indic-machine.rl" {te = p+1;{ found_syllable (symbol_cluster); }} break; case 18: -#line 87 "hb-ot-shape-complex-indic-machine.rl" +#line 88 "hb-ot-shape-complex-indic-machine.rl" {te = p+1;{ found_syllable (broken_cluster); }} break; case 11: -#line 88 "hb-ot-shape-complex-indic-machine.rl" +#line 89 "hb-ot-shape-complex-indic-machine.rl" {te = p+1;{ found_syllable (non_indic_cluster); }} break; case 13: -#line 83 "hb-ot-shape-complex-indic-machine.rl" +#line 84 "hb-ot-shape-complex-indic-machine.rl" {te = p;p--;{ found_syllable (consonant_syllable); }} break; case 15: -#line 84 "hb-ot-shape-complex-indic-machine.rl" +#line 85 "hb-ot-shape-complex-indic-machine.rl" {te = p;p--;{ found_syllable (vowel_syllable); }} break; case 20: -#line 85 "hb-ot-shape-complex-indic-machine.rl" +#line 86 "hb-ot-shape-complex-indic-machine.rl" {te = p;p--;{ found_syllable (standalone_cluster); }} break; case 23: -#line 86 "hb-ot-shape-complex-indic-machine.rl" +#line 87 "hb-ot-shape-complex-indic-machine.rl" {te = p;p--;{ found_syllable (symbol_cluster); }} break; case 17: -#line 87 "hb-ot-shape-complex-indic-machine.rl" +#line 88 "hb-ot-shape-complex-indic-machine.rl" {te = p;p--;{ found_syllable (broken_cluster); }} break; case 19: -#line 88 "hb-ot-shape-complex-indic-machine.rl" +#line 89 "hb-ot-shape-complex-indic-machine.rl" {te = p;p--;{ found_syllable (non_indic_cluster); }} break; case 1: -#line 83 "hb-ot-shape-complex-indic-machine.rl" +#line 84 "hb-ot-shape-complex-indic-machine.rl" {{p = ((te))-1;}{ found_syllable (consonant_syllable); }} break; case 3: -#line 84 "hb-ot-shape-complex-indic-machine.rl" +#line 85 "hb-ot-shape-complex-indic-machine.rl" {{p = ((te))-1;}{ found_syllable (vowel_syllable); }} break; case 7: -#line 85 "hb-ot-shape-complex-indic-machine.rl" +#line 86 "hb-ot-shape-complex-indic-machine.rl" {{p = ((te))-1;}{ found_syllable (standalone_cluster); }} break; case 8: -#line 86 "hb-ot-shape-complex-indic-machine.rl" +#line 87 "hb-ot-shape-complex-indic-machine.rl" {{p = ((te))-1;}{ found_syllable (symbol_cluster); }} break; case 4: -#line 87 "hb-ot-shape-complex-indic-machine.rl" +#line 88 "hb-ot-shape-complex-indic-machine.rl" {{p = ((te))-1;}{ found_syllable (broken_cluster); }} break; case 5: @@ -1068,22 +1148,22 @@ _eof_trans: case 22: #line 1 "NONE" {te = p+1;} -#line 83 "hb-ot-shape-complex-indic-machine.rl" +#line 84 "hb-ot-shape-complex-indic-machine.rl" {act = 1;} break; case 6: #line 1 "NONE" {te = p+1;} -#line 87 "hb-ot-shape-complex-indic-machine.rl" +#line 88 "hb-ot-shape-complex-indic-machine.rl" {act = 5;} break; case 12: #line 1 "NONE" {te = p+1;} -#line 88 "hb-ot-shape-complex-indic-machine.rl" +#line 89 "hb-ot-shape-complex-indic-machine.rl" {act = 6;} break; -#line 1087 "hb-ot-shape-complex-indic-machine.hh" +#line 1167 "hb-ot-shape-complex-indic-machine.hh" } _again: @@ -1092,7 +1172,7 @@ _again: #line 1 "NONE" {ts = 0;} break; -#line 1096 "hb-ot-shape-complex-indic-machine.hh" +#line 1176 "hb-ot-shape-complex-indic-machine.hh" } if ( ++p != pe ) @@ -1108,7 +1188,7 @@ _again: } -#line 120 "hb-ot-shape-complex-indic-machine.rl" +#line 121 "hb-ot-shape-complex-indic-machine.rl" } diff --git a/src/hb-ot-shape-complex-indic-machine.rl b/src/hb-ot-shape-complex-indic-machine.rl index c5d945d4e..f7e022b98 100644 --- a/src/hb-ot-shape-complex-indic-machine.rl +++ b/src/hb-ot-shape-complex-indic-machine.rl @@ -52,6 +52,7 @@ DOTTEDCIRCLE = 12; RS = 13; Repha = 15; Ra = 16; +CM = 17; Symbol= 18; CS = 19; @@ -67,10 +68,10 @@ matra_group = z{0,3}.M.N?.(H | forced_rakar)?; syllable_tail = (z?.SM.SM?.ZWNJ?)? A{0,3}?; halant_group = (z?.H.(ZWJ.N?)?); final_halant_group = halant_group | H.ZWNJ; +medial_group = CM?; halant_or_matra_group = (final_halant_group | matra_group{0,4}); -complex_syllable_tail = (halant_group.cn){0,4} halant_or_matra_group syllable_tail; - +complex_syllable_tail = (halant_group.cn){0,4} medial_group halant_or_matra_group syllable_tail; consonant_syllable = (Repha|CS)? cn complex_syllable_tail; vowel_syllable = reph? V.n? (ZWJ | complex_syllable_tail); diff --git a/src/hb-ot-shape-complex-indic.cc b/src/hb-ot-shape-complex-indic.cc index d421555fb..1fd8fc670 100644 --- a/src/hb-ot-shape-complex-indic.cc +++ b/src/hb-ot-shape-complex-indic.cc @@ -720,7 +720,7 @@ initial_reordering_consonant_syllable (const hb_ot_shape_plan_t *plan, indic_position_t last_pos = POS_START; for (unsigned int i = start; i < end; i++) { - if ((FLAG_UNSAFE (info[i].indic_category()) & (JOINER_FLAGS | FLAG (OT_N) | FLAG (OT_RS) | FLAG (OT_H)))) + if ((FLAG_UNSAFE (info[i].indic_category()) & (JOINER_FLAGS | FLAG (OT_N) | FLAG (OT_RS) | MEDIAL_FLAGS | FLAG (OT_H)))) { info[i].indic_position() = last_pos; if (unlikely (info[i].indic_category() == OT_H && diff --git a/src/hb-ot-shape-complex-indic.hh b/src/hb-ot-shape-complex-indic.hh index 136e34991..13ab51679 100644 --- a/src/hb-ot-shape-complex-indic.hh +++ b/src/hb-ot-shape-complex-indic.hh @@ -62,17 +62,19 @@ enum indic_category_t { OT_Coeng = 14, /* Khmer-style Virama. */ OT_Repha = 15, /* Atomically-encoded logical or visual repha. */ OT_Ra = 16, - OT_CM = 17, /* Consonant-Medial; Unused by Indic shaper. */ + OT_CM = 17, /* Consonant-Medial. */ OT_Symbol = 18, /* Avagraha, etc that take marks (SM,A,VD). */ OT_CS = 19 }; +#define MEDIAL_FLAGS (FLAG (OT_CM)) + /* Note: * * We treat Vowels and placeholders as if they were consonants. This is safe because Vowels * cannot happen in a consonant syllable. The plus side however is, we can call the * consonant syllable logic from the vowel syllable function and get it all right! */ -#define CONSONANT_FLAGS (FLAG (OT_C) | FLAG (OT_CS) | FLAG (OT_Ra) | FLAG (OT_V) | FLAG (OT_PLACEHOLDER) | FLAG (OT_DOTTEDCIRCLE)) +#define CONSONANT_FLAGS (FLAG (OT_C) | FLAG (OT_CS) | FLAG (OT_Ra) | MEDIAL_FLAGS | FLAG (OT_V) | FLAG (OT_PLACEHOLDER) | FLAG (OT_DOTTEDCIRCLE)) #define JOINER_FLAGS (FLAG (OT_ZWJ) | FLAG (OT_ZWNJ)) diff --git a/test/shaping/data/in-house/fonts/f75c4b05a0a4d67c1a808081ae3d74a9c66509e8.ttf b/test/shaping/data/in-house/fonts/f75c4b05a0a4d67c1a808081ae3d74a9c66509e8.ttf new file mode 100644 index 0000000000000000000000000000000000000000..ffbbc0e294258f0837a96f945ab6baf09b404cbb GIT binary patch literal 1924 zcma)7YfM{Z7=FLgbJ|k6;<#;5a4oR%V=$msb|DVLbu1zqRZQmMID?kLD3D$TOj)Ex z{}?lZMq@NtOlEYMF8k?{nccF4DCp{9bQpuoWQoonn%HI1o6mPn%Qmtfo|E(SdEW26 zpZ9zvMnoohjWpEjX@6!n&Cz>AS`pISo_$^3^OwGgfIkcVtH=97+y3R9y+pb?@SZMj zgDrM7;wI8efcN$V<*_nLX%PG;@b&>YJVrX)B;5ly4h&EF=ML@o4fszY_D8@c_v>BX z{{wwJWUc@NMNZvj@aMs60>Q|HaBk&G@Lk}=!=rsNyUePH1n$oilqbe$6MGDt$1{(} zL0{!Q*Eo?_0so<~(QstjTkk?&n!)}6N!h=FUldoUn4Y9fWF`yroEE30%u-fPWu>CU zT;p_*q}7&{mfLMEr^Bi#(Fon?lafDu@?Tbvxskb<`8gA3Mz$xu^xg7O{MM@D3&)ofZ;d*@j6w|dfUYVMnetr7bRiQem75*YIrAsYlHzJF5^aNEtCE4_12ThlajkF4K=wj{17Xl-=dJd7Pu; zKtg$Hk%UxSyp!#~8!{pmF{`z@x=L5;vfHS{ojKEV<&(`ueL=D1cUJKf6YpIAfqf`s zZe@NcA|ahk$FeKPaXtEjCa68gCFpdx-I>qUIkwk1Dg%<&wD@~Y ze>xcQl8~H326fqabQcrCl$zXDm~ME;S?wcAPR{WX-#%t7df(t`-qQRy53QWbo3YU`CGH)7N*Tly_lPI2HGbEwUA4qoCTvUIB82N>1Omv|nh>|D>Vm=a& z#xGA-mxp#4%!a5MLszG1hghjlT)}J;nc<&HG>X(nA~k~>&5b%1)wkEQ8A+$Fck|pJz_bmp}jPd!{P%v66RqsC)Xmod-ODo z(ilym|AY800~CSOMvc@&b|4RSMNeN|AEC`W!ZHxr@@X;Wd4iAq9sWfNBv%wDh z>M^SU#exAde7hm6{JiF|)m*j2vK^Roo(mK10`Z*k`fWgYeeRW?11!z|0aX1{Ko9B{ zL%tEys@BF=ek)cPuB%vSW);NitHGo`x;}2jP*)h4b34`cek>k|*Ce9D_!5CQrvCuh COLnLL literal 0 HcmV?d00001 diff --git a/test/shaping/data/in-house/tests/indic-syllable.tests b/test/shaping/data/in-house/tests/indic-syllable.tests index 4c7d6513e..264983bf0 100644 --- a/test/shaping/data/in-house/tests/indic-syllable.tests +++ b/test/shaping/data/in-house/tests/indic-syllable.tests @@ -6,3 +6,5 @@ ../fonts/1735326da89f0818cd8c51a0600e9789812c0f94.ttf::U+0A51:[uni25CC=0+1044|udaatguru=0+0] ../fonts/1735326da89f0818cd8c51a0600e9789812c0f94.ttf::U+25CC,U+0A51:[uni25CC=0+1044|udaatguru=0+0] ../fonts/81c368a33816fb20e9f647e8f24e2180f4720263.ttf:--no-glyph-names:U+0C80,U+0C82:[1=0+502|2=0+502] +../fonts/f75c4b05a0a4d67c1a808081ae3d74a9c66509e8.ttf::U+0A20,U+0A75,U+0A47:[tthaguru=0+1352|yakashguru=0@-90,0+0|eematraguru=0@-411,0+0] +../fonts/f75c4b05a0a4d67c1a808081ae3d74a9c66509e8.ttf::U+0A20,U+0A75,U+0A42:[tthaguru=0+1352|yakashuuguru=0+0] From d2db71fdc4764eecf8320cf465ee0e4254146b6e Mon Sep 17 00:00:00 2001 From: Behdad Esfahbod Date: Thu, 28 Mar 2019 21:00:58 -0700 Subject: [PATCH 099/101] Use internal bsearch() for language tags Fixes https://github.com/harfbuzz/harfbuzz/pull/1639 --- src/hb-ot-tag.cc | 46 ++++++++++++++++++++++------------------------ 1 file changed, 22 insertions(+), 24 deletions(-) diff --git a/src/hb-ot-tag.cc b/src/hb-ot-tag.cc index d04e5329a..751ccab0b 100644 --- a/src/hb-ot-tag.cc +++ b/src/hb-ot-tag.cc @@ -171,24 +171,6 @@ hb_ot_tag_to_script (hb_tag_t tag) /* hb_language_t */ -static int -lang_compare_first_component (const void *pa, - const void *pb) -{ - const char *a = (const char *) pa; - const char *b = (const char *) pb; - unsigned int da, db; - const char *p; - - p = strchr (a, '-'); - da = p ? (unsigned int) (p - a) : strlen (a); - - p = strchr (b, '-'); - db = p ? (unsigned int) (p - b) : strlen (b); - - return strncmp (a, b, MAX (da, db)); -} - static bool subtag_matches (const char *lang_str, const char *limit, @@ -213,10 +195,28 @@ lang_matches (const char *lang_str, const char *spec) (lang_str[len] == '\0' || lang_str[len] == '-'); } -typedef struct { +struct LangTag +{ char language[4]; hb_tag_t tags[HB_OT_MAX_TAGS_PER_LANGUAGE]; -} LangTag; + + int cmp (const char *a) const + { + const char *b = this->language; + unsigned int da, db; + const char *p; + + p = strchr (a, '-'); + da = p ? (unsigned int) (p - a) : strlen (a); + + p = strchr (b, '-'); + db = p ? (unsigned int) (p - b) : strlen (b); + + return strncmp (a, b, MAX (da, db)); + } + int cmp (const LangTag *that) const + { return cmp (that->language); } +}; #include "hb-ot-tag-table.hh" @@ -263,9 +263,7 @@ hb_ot_tags_from_language (const char *lang_str, ISALPHA (s[1])) lang_str = s + 1; } - lang_tag = (LangTag *) bsearch (lang_str, ot_languages, - ARRAY_LENGTH (ot_languages), sizeof (LangTag), - lang_compare_first_component); + lang_tag = hb_sorted_array (ot_languages).bsearch (lang_str); if (lang_tag) { unsigned int i; @@ -507,7 +505,7 @@ test_langs_sorted () { for (unsigned int i = 1; i < ARRAY_LENGTH (ot_languages); i++) { - int c = lang_compare_first_component (ot_languages[i-1].language, ot_languages[i].language); + int c = ot_languages[i].cmp (&ot_languages[i - 1]); if (c >= 0) { fprintf (stderr, "ot_languages not sorted at index %d: %s %d %s\n", From d6fc1d49aa099104a889c96bc9087c21d8fc0960 Mon Sep 17 00:00:00 2001 From: Behdad Esfahbod Date: Thu, 28 Mar 2019 21:21:26 -0700 Subject: [PATCH 100/101] 2.4.0 --- NEWS | 11 +++++++++++ configure.ac | 2 +- src/hb-buffer.h | 2 +- src/hb-version.h | 6 +++--- 4 files changed, 16 insertions(+), 5 deletions(-) diff --git a/NEWS b/NEWS index ef87dad76..f3e424fbc 100644 --- a/NEWS +++ b/NEWS @@ -1,3 +1,14 @@ +Overview of changes leading to 2.4.0 +Monday, March 25, 2019 +==================================== +- Unicode 12. +- Misc fixes. +- Subsetter improvements. +- New API: +HB_BUFFER_FLAG_DO_NOT_INSERT_DOTTED_CIRCLE +hb_directwrite_face_create() + + Overview of changes leading to 2.3.1 Wednesday, January 30, 2019 ==================================== diff --git a/configure.ac b/configure.ac index 718f1d049..b51ac4a93 100644 --- a/configure.ac +++ b/configure.ac @@ -1,6 +1,6 @@ AC_PREREQ([2.64]) AC_INIT([HarfBuzz], - [2.3.1], + [2.4.0], [https://github.com/harfbuzz/harfbuzz/issues/new], [harfbuzz], [http://harfbuzz.org/]) diff --git a/src/hb-buffer.h b/src/hb-buffer.h index 43aeb99db..ac1d4525a 100644 --- a/src/hb-buffer.h +++ b/src/hb-buffer.h @@ -287,7 +287,7 @@ hb_buffer_guess_segment_properties (hb_buffer_t *buffer); * @HB_BUFFER_FLAG_DO_NOT_INSERT_DOTTED_CIRCLE: * flag indicating that a dotted circle should * not be inserted in the rendering of incorrect - * character sequences (such at <0905 093E>). Since: REPLACEME + * character sequences (such at <0905 093E>). Since: 2.4 * * Since: 0.9.20 */ diff --git a/src/hb-version.h b/src/hb-version.h index 13db8ceac..783e37b08 100644 --- a/src/hb-version.h +++ b/src/hb-version.h @@ -37,10 +37,10 @@ HB_BEGIN_DECLS #define HB_VERSION_MAJOR 2 -#define HB_VERSION_MINOR 3 -#define HB_VERSION_MICRO 1 +#define HB_VERSION_MINOR 4 +#define HB_VERSION_MICRO 0 -#define HB_VERSION_STRING "2.3.1" +#define HB_VERSION_STRING "2.4.0" #define HB_VERSION_ATLEAST(major,minor,micro) \ ((major)*10000+(minor)*100+(micro) <= \ From ddb84dcece8a12a5615cb1609030a52387bd2fce Mon Sep 17 00:00:00 2001 From: Michiharu Ariza Date: Fri, 29 Mar 2019 10:32:42 -0700 Subject: [PATCH 101/101] fix gvar fuzz bug --- .gitignore | 1 + src/hb-ot-var-gvar-table.hh | 8 +++++++- 2 files changed, 8 insertions(+), 1 deletion(-) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..da127e065 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +libtool diff --git a/src/hb-ot-var-gvar-table.hh b/src/hb-ot-var-gvar-table.hh index 354edf99f..9def7a3c2 100644 --- a/src/hb-ot-var-gvar-table.hh +++ b/src/hb-ot-var-gvar-table.hh @@ -511,7 +511,13 @@ struct gvar } unsigned int get_glyph_var_data_length (unsigned int glyph) const - { return get_offset (glyph+1) - get_offset (glyph); } + { + unsigned int end_offset = get_offset (glyph+1); + unsigned int start_offset = get_offset (glyph); + if (unlikely (start_offset > end_offset || end_offset > get_offset(glyphCount))) + return 0; + return end_offset - start_offset; + } const HBUINT32 *get_long_offset_array () const { return (const HBUINT32 *)&offsetZ; } const HBUINT16 *get_short_offset_array () const { return (const HBUINT16 *)&offsetZ; }