Merge pull request #3795 from googlefonts/instance_GDEF_GPOS

[instancing] update GDEF/GPOS tables and a few fixes for glyf instancing
This commit is contained in:
Behdad Esfahbod 2022-09-01 13:12:25 -06:00 committed by GitHub
commit fd107bddb0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
32 changed files with 457 additions and 157 deletions

View File

@ -58,8 +58,7 @@ struct Anchor
return_trace (bool (reinterpret_cast<Anchor *> (u.format1.copy (c->serializer))));
}
return_trace (bool (reinterpret_cast<Anchor *> (u.format2.copy (c->serializer))));
case 3: return_trace (bool (reinterpret_cast<Anchor *> (u.format3.copy (c->serializer,
c->plan->layout_variation_idx_map))));
case 3: return_trace (u.format3.subset (c));
default:return_trace (false);
}
}

View File

@ -41,24 +41,54 @@ struct AnchorFormat3
*y += (this+yDeviceTable).get_y_delta (font, c->var_store, c->var_store_cache);
}
AnchorFormat3* copy (hb_serialize_context_t *c,
const hb_map_t *layout_variation_idx_map) const
bool subset (hb_subset_context_t *c) const
{
TRACE_SERIALIZE (this);
if (!layout_variation_idx_map) return_trace (nullptr);
TRACE_SUBSET (this);
auto *out = c->serializer->start_embed (*this);
if (unlikely (!out)) return_trace (false);
if (unlikely (!c->serializer->embed (format))) return_trace (false);
if (unlikely (!c->serializer->embed (xCoordinate))) return_trace (false);
if (unlikely (!c->serializer->embed (yCoordinate))) return_trace (false);
auto *out = c->embed<AnchorFormat3> (this);
if (unlikely (!out)) return_trace (nullptr);
unsigned x_varidx = xDeviceTable ? (this+xDeviceTable).get_variation_index () : HB_OT_LAYOUT_NO_VARIATIONS_INDEX;
if (c->plan->layout_variation_idx_delta_map->has (x_varidx))
{
int delta = hb_second (c->plan->layout_variation_idx_delta_map->get (x_varidx));
if (delta != 0)
{
if (!c->serializer->check_assign (out->xCoordinate, xCoordinate + delta,
HB_SERIALIZE_ERROR_INT_OVERFLOW))
return_trace (false);
}
}
out->xDeviceTable.serialize_copy (c, xDeviceTable, this, 0, hb_serialize_context_t::Head, layout_variation_idx_map);
out->yDeviceTable.serialize_copy (c, yDeviceTable, this, 0, hb_serialize_context_t::Head, layout_variation_idx_map);
unsigned y_varidx = yDeviceTable ? (this+yDeviceTable).get_variation_index () : HB_OT_LAYOUT_NO_VARIATIONS_INDEX;
if (c->plan->layout_variation_idx_delta_map->has (y_varidx))
{
int delta = hb_second (c->plan->layout_variation_idx_delta_map->get (y_varidx));
if (delta != 0)
{
if (!c->serializer->check_assign (out->yCoordinate, yCoordinate + delta,
HB_SERIALIZE_ERROR_INT_OVERFLOW))
return_trace (false);
}
}
if (c->plan->all_axes_pinned)
return_trace (c->serializer->check_assign (out->format, 1, HB_SERIALIZE_ERROR_INT_OVERFLOW));
if (!c->serializer->embed (xDeviceTable)) return_trace (false);
if (!c->serializer->embed (yDeviceTable)) return_trace (false);
out->xDeviceTable.serialize_copy (c->serializer, xDeviceTable, this, 0, hb_serialize_context_t::Head, c->plan->layout_variation_idx_delta_map);
out->yDeviceTable.serialize_copy (c->serializer, yDeviceTable, this, 0, hb_serialize_context_t::Head, c->plan->layout_variation_idx_delta_map);
return_trace (out);
}
void collect_variation_indices (hb_collect_variation_indices_context_t *c) const
{
(this+xDeviceTable).collect_variation_indices (c->layout_variation_indices);
(this+yDeviceTable).collect_variation_indices (c->layout_variation_indices);
(this+xDeviceTable).collect_variation_indices (c);
(this+yDeviceTable).collect_variation_indices (c);
}
};

View File

@ -22,7 +22,8 @@ template<typename Iterator, typename SrcLookup>
static void SinglePos_serialize (hb_serialize_context_t *c,
const SrcLookup *src,
Iterator it,
const hb_map_t *layout_variation_idx_map);
const hb_hashmap_t<unsigned, hb_pair_t<unsigned, int>> *layout_variation_idx_delta_map,
bool all_axes_pinned);
}

View File

@ -127,6 +127,12 @@ struct PairPosFormat1_3
out->valueFormat[1] = newFormats.second;
}
if (c->plan->all_axes_pinned)
{
out->valueFormat[0] = out->valueFormat[0].drop_device_table_flags ();
out->valueFormat[1] = out->valueFormat[1].drop_device_table_flags ();
}
hb_sorted_vector_t<hb_codepoint_t> new_coverage;
+ hb_zip (this+coverage, pairSet)

View File

@ -274,13 +274,19 @@ struct PairPosFormat2_4
out->valueFormat1 = newFormats.first;
out->valueFormat2 = newFormats.second;
if (c->plan->all_axes_pinned)
{
out->valueFormat1 = out->valueFormat1.drop_device_table_flags ();
out->valueFormat2 = out->valueFormat2.drop_device_table_flags ();
}
for (unsigned class1_idx : + hb_range ((unsigned) class1Count) | hb_filter (klass1_map))
{
for (unsigned class2_idx : + hb_range ((unsigned) class2Count) | hb_filter (klass2_map))
{
unsigned idx = (class1_idx * (unsigned) class2Count + class2_idx) * (len1 + len2);
valueFormat1.copy_values (c->serializer, newFormats.first, this, &values[idx], c->plan->layout_variation_idx_map);
valueFormat2.copy_values (c->serializer, newFormats.second, this, &values[idx + len1], c->plan->layout_variation_idx_map);
valueFormat1.copy_values (c->serializer, out->valueFormat1, this, &values[idx], c->plan->layout_variation_idx_delta_map);
valueFormat2.copy_values (c->serializer, out->valueFormat2, this, &values[idx + len1], c->plan->layout_variation_idx_delta_map);
}
}

View File

@ -163,7 +163,7 @@ struct PairSet
newFormats,
len1,
&glyph_map,
c->plan->layout_variation_idx_map
c->plan->layout_variation_idx_delta_map
};
const PairValueRecord *record = &firstPairValueRecord;

View File

@ -34,7 +34,7 @@ struct PairValueRecord
const ValueFormat *newFormats;
unsigned len1; /* valueFormats[0].get_len() */
const hb_map_t *glyph_map;
const hb_map_t *layout_variation_idx_map;
const hb_hashmap_t<unsigned, hb_pair_t<unsigned, int>> *layout_variation_idx_delta_map;
};
bool subset (hb_subset_context_t *c,
@ -50,12 +50,12 @@ struct PairValueRecord
closure->valueFormats[0].copy_values (s,
closure->newFormats[0],
closure->base, &values[0],
closure->layout_variation_idx_map);
closure->layout_variation_idx_delta_map);
closure->valueFormats[1].copy_values (s,
closure->newFormats[1],
closure->base,
&values[closure->len1],
closure->layout_variation_idx_map);
closure->layout_variation_idx_delta_map);
return_trace (true);
}

View File

@ -38,12 +38,16 @@ struct SinglePos
void serialize (hb_serialize_context_t *c,
const SrcLookup* src,
Iterator glyph_val_iter_pairs,
const hb_map_t *layout_variation_idx_map)
const hb_hashmap_t<unsigned, hb_pair_t<unsigned, int>> *layout_variation_idx_delta_map,
bool all_axes_pinned)
{
if (unlikely (!c->extend_min (u.format))) return;
unsigned format = 2;
ValueFormat new_format = src->get_value_format ();
if (all_axes_pinned)
new_format = new_format.drop_device_table_flags ();
if (glyph_val_iter_pairs)
format = get_format (glyph_val_iter_pairs);
@ -53,13 +57,13 @@ struct SinglePos
src,
glyph_val_iter_pairs,
new_format,
layout_variation_idx_map);
layout_variation_idx_delta_map);
return;
case 2: u.format2.serialize (c,
src,
glyph_val_iter_pairs,
new_format,
layout_variation_idx_map);
layout_variation_idx_delta_map);
return;
default:return;
}
@ -84,8 +88,9 @@ static void
SinglePos_serialize (hb_serialize_context_t *c,
const SrcLookup *src,
Iterator it,
const hb_map_t *layout_variation_idx_map)
{ c->start_embed<SinglePos> ()->serialize (c, src, it, layout_variation_idx_map); }
const hb_hashmap_t<unsigned, hb_pair_t<unsigned, int>> *layout_variation_idx_delta_map,
bool all_axes_pinned)
{ c->start_embed<SinglePos> ()->serialize (c, src, it, layout_variation_idx_delta_map, all_axes_pinned); }
}

View File

@ -87,7 +87,7 @@ struct SinglePosFormat1
const SrcLookup *src,
Iterator it,
ValueFormat newFormat,
const hb_map_t *layout_variation_idx_map)
const hb_hashmap_t<unsigned, hb_pair_t<unsigned, int>> *layout_variation_idx_delta_map)
{
if (unlikely (!c->extend_min (this))) return;
if (unlikely (!c->check_assign (valueFormat,
@ -96,7 +96,7 @@ struct SinglePosFormat1
for (const hb_array_t<const Value>& _ : + it | hb_map (hb_second))
{
src->get_value_format ().copy_values (c, newFormat, src, &_, layout_variation_idx_map);
src->get_value_format ().copy_values (c, newFormat, src, &_, layout_variation_idx_delta_map);
// Only serialize the first entry in the iterator, the rest are assumed to
// be the same.
break;
@ -126,7 +126,7 @@ struct SinglePosFormat1
;
bool ret = bool (it);
SinglePos_serialize (c->serializer, this, it, c->plan->layout_variation_idx_map);
SinglePos_serialize (c->serializer, this, it, c->plan->layout_variation_idx_delta_map, c->plan->all_axes_pinned);
return_trace (ret);
}
};

View File

@ -99,7 +99,7 @@ struct SinglePosFormat2
const SrcLookup *src,
Iterator it,
ValueFormat newFormat,
const hb_map_t *layout_variation_idx_map)
const hb_hashmap_t<unsigned, hb_pair_t<unsigned, int>> *layout_variation_idx_delta_map)
{
auto out = c->extend_min (this);
if (unlikely (!out)) return;
@ -109,7 +109,7 @@ struct SinglePosFormat2
+ it
| hb_map (hb_second)
| hb_apply ([&] (hb_array_t<const Value> _)
{ src->get_value_format ().copy_values (c, newFormat, src, &_, layout_variation_idx_map); })
{ src->get_value_format ().copy_values (c, newFormat, src, &_, layout_variation_idx_delta_map); })
;
auto glyphs =
@ -141,7 +141,7 @@ struct SinglePosFormat2
;
bool ret = bool (it);
SinglePos_serialize (c->serializer, this, it, c->plan->layout_variation_idx_map);
SinglePos_serialize (c->serializer, this, it, c->plan->layout_variation_idx_delta_map, c->plan->all_axes_pinned);
return_trace (ret);
}
};

View File

@ -163,30 +163,50 @@ struct ValueFormat : HBUINT16
unsigned int new_format,
const void *base,
const Value *values,
const hb_map_t *layout_variation_idx_map) const
const hb_hashmap_t<unsigned, hb_pair_t<unsigned, int>> *layout_variation_idx_delta_map) const
{
unsigned int format = *this;
if (!format) return;
if (format & xPlacement) copy_value (c, new_format, xPlacement, *values++);
if (format & yPlacement) copy_value (c, new_format, yPlacement, *values++);
if (format & xAdvance) copy_value (c, new_format, xAdvance, *values++);
if (format & yAdvance) copy_value (c, new_format, yAdvance, *values++);
HBINT16 *x_placement = nullptr, *y_placement = nullptr, *x_adv = nullptr, *y_adv = nullptr;
if (format & xPlacement) x_placement = copy_value (c, new_format, xPlacement, *values++);
if (format & yPlacement) y_placement = copy_value (c, new_format, yPlacement, *values++);
if (format & xAdvance) x_adv = copy_value (c, new_format, xAdvance, *values++);
if (format & yAdvance) y_adv = copy_value (c, new_format, yAdvance, *values++);
if (format & xPlaDevice) copy_device (c, base, values++, layout_variation_idx_map);
if (format & yPlaDevice) copy_device (c, base, values++, layout_variation_idx_map);
if (format & xAdvDevice) copy_device (c, base, values++, layout_variation_idx_map);
if (format & yAdvDevice) copy_device (c, base, values++, layout_variation_idx_map);
if (format & xPlaDevice)
{
add_delta_to_value (x_placement, base, values, layout_variation_idx_delta_map);
copy_device (c, base, values++, layout_variation_idx_delta_map, new_format, xPlaDevice);
}
void copy_value (hb_serialize_context_t *c,
if (format & yPlaDevice)
{
add_delta_to_value (y_placement, base, values, layout_variation_idx_delta_map);
copy_device (c, base, values++, layout_variation_idx_delta_map, new_format, yPlaDevice);
}
if (format & xAdvDevice)
{
add_delta_to_value (x_adv, base, values, layout_variation_idx_delta_map);
copy_device (c, base, values++, layout_variation_idx_delta_map, new_format, xAdvDevice);
}
if (format & yAdvDevice)
{
add_delta_to_value (y_adv, base, values, layout_variation_idx_delta_map);
copy_device (c, base, values++, layout_variation_idx_delta_map, new_format, yAdvDevice);
}
}
HBINT16* copy_value (hb_serialize_context_t *c,
unsigned int new_format,
Flags flag,
Value value) const
{
// Filter by new format.
if (!(new_format & flag)) return;
c->copy (value);
if (!(new_format & flag)) return nullptr;
return reinterpret_cast<HBINT16 *> (c->copy (value));
}
void collect_variation_indices (hb_collect_variation_indices_context_t *c,
@ -201,31 +221,40 @@ struct ValueFormat : HBUINT16
if (format & yAdvance) i++;
if (format & xPlaDevice)
{
(base + get_device (&(values[i]))).collect_variation_indices (c->layout_variation_indices);
(base + get_device (&(values[i]))).collect_variation_indices (c);
i++;
}
if (format & ValueFormat::yPlaDevice)
{
(base + get_device (&(values[i]))).collect_variation_indices (c->layout_variation_indices);
(base + get_device (&(values[i]))).collect_variation_indices (c);
i++;
}
if (format & ValueFormat::xAdvDevice)
{
(base + get_device (&(values[i]))).collect_variation_indices (c->layout_variation_indices);
(base + get_device (&(values[i]))).collect_variation_indices (c);
i++;
}
if (format & ValueFormat::yAdvDevice)
{
(base + get_device (&(values[i]))).collect_variation_indices (c->layout_variation_indices);
(base + get_device (&(values[i]))).collect_variation_indices (c);
i++;
}
}
unsigned drop_device_table_flags () const
{
unsigned format = *this;
for (unsigned flag = xPlaDevice; flag <= yAdvDevice; flag = flag << 1)
format = format & ~flag;
return format;
}
private:
bool sanitize_value_devices (hb_sanitize_context_t *c, const void *base, const Value *values) const
{
@ -254,9 +283,27 @@ struct ValueFormat : HBUINT16
return *static_cast<const Offset16To<Device> *> (value);
}
bool copy_device (hb_serialize_context_t *c, const void *base,
const Value *src_value, const hb_map_t *layout_variation_idx_map) const
void add_delta_to_value (HBINT16 *value,
const void *base,
const Value *src_value,
const hb_hashmap_t<unsigned, hb_pair_t<unsigned, int>> *layout_variation_idx_delta_map) const
{
if (!value) return;
unsigned varidx = (base + get_device (src_value)).get_variation_index ();
hb_pair_t<unsigned, int> *varidx_delta;
if (!layout_variation_idx_delta_map->has (varidx, &varidx_delta)) return;
*value += hb_second (*varidx_delta);
}
bool copy_device (hb_serialize_context_t *c, const void *base,
const Value *src_value,
const hb_hashmap_t<unsigned, hb_pair_t<unsigned, int>> *layout_variation_idx_delta_map,
unsigned int new_format, Flags flag) const
{
// Filter by new format.
if (!(new_format & flag)) return true;
Value *dst_value = c->copy (*src_value);
if (!dst_value) return false;
@ -264,7 +311,7 @@ struct ValueFormat : HBUINT16
*dst_value = 0;
c->push ();
if ((base + get_device (src_value)).copy (c, layout_variation_idx_map))
if ((base + get_device (src_value)).copy (c, layout_variation_idx_delta_map))
{
c->add_link (*dst_value, c->pop_pack ());
return true;

View File

@ -105,9 +105,10 @@ struct CompositeGlyphRecord
}
}
void apply_delta_to_offsets (const contour_point_t &p_delta)
unsigned compile_with_deltas (const contour_point_t &p_delta,
char *out) const
{
HBINT8 *p = &StructAfter<HBINT8> (flags);
const HBINT8 *p = &StructAfter<const HBINT8> (flags);
#ifndef HB_NO_BEYOND_64K
if (flags & GID_IS_24BIT)
p += HBGlyphID24::static_size;
@ -115,17 +116,54 @@ struct CompositeGlyphRecord
#endif
p += HBGlyphID16::static_size;
unsigned len = get_size ();
unsigned len_before_val = (const char *)p - (const char *)this;
if (flags & ARG_1_AND_2_ARE_WORDS)
{
HBINT16 *px = reinterpret_cast<HBINT16 *> (p);
px[0] += roundf (p_delta.x);
px[1] += roundf (p_delta.y);
// no overflow, copy and update value with deltas
memcpy (out, this, len);
const HBINT16 *px = reinterpret_cast<const HBINT16 *> (p);
HBINT16 *o = reinterpret_cast<HBINT16 *> (out + len_before_val);
o[0] = px[0] + roundf (p_delta.x);
o[1] = px[1] + roundf (p_delta.y);
}
else
{
p[0] += roundf (p_delta.x);
p[1] += roundf (p_delta.y);
int new_x = p[0] + roundf (p_delta.x);
int new_y = p[1] + roundf (p_delta.y);
if (new_x <= 127 && new_x >= -128 &&
new_y <= 127 && new_y >= -128)
{
memcpy (out, this, len);
HBINT8 *o = reinterpret_cast<HBINT8 *> (out + len_before_val);
o[0] = new_x;
o[1] = new_y;
}
else
{
// int8 overflows after deltas applied
memcpy (out, this, len_before_val);
//update flags
CompositeGlyphRecord *o = reinterpret_cast<CompositeGlyphRecord *> (out);
o->flags = flags | ARG_1_AND_2_ARE_WORDS;
out += len_before_val;
HBINT16 new_value;
new_value = new_x;
memcpy (out, &new_value, HBINT16::static_size);
out += HBINT16::static_size;
new_value = new_y;
memcpy (out, &new_value, HBINT16::static_size);
out += HBINT16::static_size;
memcpy (out, p+2, len - len_before_val - 2);
len += 2;
}
}
return len;
}
protected:
@ -316,32 +354,56 @@ struct CompositeGlyph
const contour_point_vector_t &deltas,
hb_bytes_t &dest_bytes /* OUT */)
{
int len = source_bytes.length - GlyphHeader::static_size;
if (len <= 0 || header.numberOfContours != -1)
if (source_bytes.length <= GlyphHeader::static_size ||
header.numberOfContours != -1)
{
dest_bytes = hb_bytes_t ();
return true;
}
char *p = (char *) hb_calloc (len, sizeof (char));
if (unlikely (!p)) return false;
unsigned source_len = source_bytes.length - GlyphHeader::static_size;
memcpy (p, source_bytes.arrayZ + GlyphHeader::static_size, len);
dest_bytes = hb_bytes_t (p, len);
/* try to allocate more memories than source glyph bytes
* in case that there might be an overflow for int8 value
* and we would need to use int16 instead */
char *o = (char *) hb_calloc (source_len + source_len/2, sizeof (char));
if (unlikely (!o)) return false;
auto it = composite_iter_t (dest_bytes, (CompositeGlyphRecord *)p);
const CompositeGlyphRecord *c = reinterpret_cast<const CompositeGlyphRecord *> (source_bytes.arrayZ + GlyphHeader::static_size);
auto it = composite_iter_t (hb_bytes_t ((const char *)c, source_len), c);
unsigned i = 0;
for (auto &component : it)
char *p = o;
unsigned i = 0, source_comp_len = 0;
for (const auto &component : it)
{
if (!component.is_anchored ())
{
/* last 4 points in deltas are phantom points and should not be included*/
/* last 4 points in deltas are phantom points and should not be included */
if (i >= deltas.length - 4) return false;
const_cast<CompositeGlyphRecord &> (component).apply_delta_to_offsets (deltas[i]);
unsigned comp_len = component.get_size ();
if (component.is_anchored ())
{
memcpy (p, &component, comp_len);
p += comp_len;
}
else
{
unsigned new_len = component.compile_with_deltas (deltas[i], p);
p += new_len;
}
i++;
source_comp_len += comp_len;
}
//copy instructions if any
if (source_len > source_comp_len)
{
unsigned instr_len = source_len - source_comp_len;
memcpy (p, (const char *)c + source_comp_len, instr_len);
p += instr_len;
}
unsigned len = p - o;
dest_bytes = hb_bytes_t (o, len);
return true;
}
};

View File

@ -101,14 +101,12 @@ struct Glyph
const contour_point_vector_t &all_points,
hb_bytes_t &dest_bytes /* OUT */) const
{
if (all_points.length == 4) //Empty glyph
GlyphHeader *glyph_header = nullptr;
if (all_points.length > 4)
{
dest_bytes = hb_bytes_t ();
return true;
}
GlyphHeader *glyph_header = (GlyphHeader *) hb_calloc (1, GlyphHeader::static_size);
glyph_header = (GlyphHeader *) hb_calloc (1, GlyphHeader::static_size);
if (unlikely (!glyph_header)) return false;
}
int xMin, xMax;
xMin = xMax = roundf (all_points[0].x);
@ -128,6 +126,11 @@ struct Glyph
update_mtx (plan, xMin, yMax, all_points);
/*for empty glyphs: all_points only include phantom points.
*just update metrics and then return */
if (all_points.length == 4)
return true;
glyph_header->numberOfContours = header->numberOfContours;
glyph_header->xMin = xMin;
glyph_header->yMin = yMin;
@ -145,7 +148,7 @@ struct Glyph
hb_bytes_t &dest_end /* OUT */) const
{
contour_point_vector_t all_points, deltas;
get_points (font, glyf, all_points, &deltas);
get_points (font, glyf, all_points, &deltas, false);
switch (type) {
case COMPOSITE:
@ -161,8 +164,11 @@ struct Glyph
return false;
break;
default:
//no need to compile empty glyph (.notdef)
return true;
/* set empty bytes for empty glyph
* do not use source glyph's pointers */
dest_start = hb_bytes_t ();
dest_end = hb_bytes_t ();
break;
}
return compile_header_bytes (plan, all_points, dest_start);
@ -176,6 +182,7 @@ struct Glyph
bool get_points (hb_font_t *font, const accelerator_t &glyf_accelerator,
contour_point_vector_t &all_points /* OUT */,
contour_point_vector_t *deltas = nullptr, /* OUT */
bool use_my_metrics = true,
bool phantom_only = false,
unsigned int depth = 0) const
{
@ -264,11 +271,11 @@ struct Glyph
comp_points.reset ();
if (unlikely (!glyf_accelerator.glyph_for_gid (item.get_gid ())
.get_points (font, glyf_accelerator, comp_points,
deltas, phantom_only, depth + 1)))
deltas, use_my_metrics, phantom_only, depth + 1)))
return false;
/* Copy phantom points from component if USE_MY_METRICS flag set */
if (item.is_use_my_metrics ())
if (use_my_metrics && item.is_use_my_metrics ())
for (unsigned int i = 0; i < PHANTOM_COUNT; i++)
phantoms[i] = comp_points[comp_points.length - PHANTOM_COUNT + i];

View File

@ -180,7 +180,7 @@ struct glyf_accelerator_t
contour_point_vector_t all_points;
bool phantom_only = !consumer.is_consuming_contour_points ();
if (unlikely (!glyph_for_gid (gid).get_points (font, *this, all_points, nullptr, phantom_only)))
if (unlikely (!glyph_for_gid (gid).get_points (font, *this, all_points, nullptr, true, phantom_only)))
return false;
if (consumer.is_consuming_contour_points ())

View File

@ -186,6 +186,7 @@ struct hb_subset_layout_context_t :
unsigned lookup_index_count;
};
struct VariationStore;
struct hb_collect_variation_indices_context_t :
hb_dispatch_context_t<hb_collect_variation_indices_context_t>
{
@ -194,15 +195,27 @@ struct hb_collect_variation_indices_context_t :
static return_t default_return_value () { return hb_empty_t (); }
hb_set_t *layout_variation_indices;
hb_hashmap_t<unsigned, hb_pair_t<unsigned, int>> *varidx_delta_map;
hb_font_t *font;
const VariationStore *var_store;
const hb_set_t *glyph_set;
const hb_map_t *gpos_lookups;
float *store_cache;
hb_collect_variation_indices_context_t (hb_set_t *layout_variation_indices_,
hb_hashmap_t<unsigned, hb_pair_t<unsigned, int>> *varidx_delta_map_,
hb_font_t *font_,
const VariationStore *var_store_,
const hb_set_t *glyph_set_,
const hb_map_t *gpos_lookups_) :
const hb_map_t *gpos_lookups_,
float *store_cache_) :
layout_variation_indices (layout_variation_indices_),
varidx_delta_map (varidx_delta_map_),
font (font_),
var_store (var_store_),
glyph_set (glyph_set_),
gpos_lookups (gpos_lookups_) {}
gpos_lookups (gpos_lookups_),
store_cache (store_cache_) {}
};
template<typename OutputArray>
@ -2562,7 +2575,7 @@ struct VariationStore
bool serialize (hb_serialize_context_t *c,
const VariationStore *src,
const hb_array_t <hb_inc_bimap_t> &inner_maps)
const hb_array_t <const hb_inc_bimap_t> &inner_maps)
{
TRACE_SERIALIZE (this);
#ifdef HB_NO_VAR
@ -2617,7 +2630,7 @@ struct VariationStore
return_trace (true);
}
bool subset (hb_subset_context_t *c) const
bool subset (hb_subset_context_t *c, const hb_array_t<const hb_inc_bimap_t> &inner_maps) const
{
TRACE_SUBSET (this);
#ifdef HB_NO_VAR
@ -2627,22 +2640,7 @@ struct VariationStore
VariationStore *varstore_prime = c->serializer->start_embed<VariationStore> ();
if (unlikely (!varstore_prime)) return_trace (false);
const hb_set_t *variation_indices = c->plan->layout_variation_indices;
if (variation_indices->is_empty ()) return_trace (false);
hb_vector_t<hb_inc_bimap_t> inner_maps;
inner_maps.resize ((unsigned) dataSets.len);
for (unsigned idx : c->plan->layout_variation_indices->iter ())
{
uint16_t major = idx >> 16;
uint16_t minor = idx & 0xFFFF;
if (major >= inner_maps.length)
return_trace (false);
inner_maps[major].add (minor);
}
varstore_prime->serialize (c->serializer, this, inner_maps.as_array ());
varstore_prime->serialize (c->serializer, this, inner_maps);
return_trace (
!c->serializer->in_error()
@ -3168,28 +3166,36 @@ struct VariationDevice
VariationStore::cache_t *store_cache = nullptr) const
{ return font->em_scalef_y (get_delta (font, store, store_cache)); }
VariationDevice* copy (hb_serialize_context_t *c, const hb_map_t *layout_variation_idx_map) const
VariationDevice* copy (hb_serialize_context_t *c,
const hb_hashmap_t<unsigned, hb_pair_t<unsigned, int>> *layout_variation_idx_delta_map) const
{
TRACE_SERIALIZE (this);
if (!layout_variation_idx_delta_map) return_trace (nullptr);
auto snap = c->snapshot ();
auto *out = c->embed (this);
if (unlikely (!out)) return_trace (nullptr);
if (!layout_variation_idx_map || layout_variation_idx_map->is_empty ()) return_trace (out);
/* TODO Just get() and bail if NO_VARIATION. Needs to setup the map to return that. */
if (!layout_variation_idx_map->has (varIdx))
if (!layout_variation_idx_delta_map->has (varIdx))
{
c->revert (snap);
return_trace (nullptr);
}
unsigned new_idx = layout_variation_idx_map->get (varIdx);
unsigned new_idx = hb_first (layout_variation_idx_delta_map->get (varIdx));
out->varIdx = new_idx;
return_trace (out);
}
void record_variation_index (hb_set_t *layout_variation_indices) const
void collect_variation_index (hb_collect_variation_indices_context_t *c) const
{
layout_variation_indices->add (varIdx);
c->layout_variation_indices->add (varIdx);
int delta = 0;
if (c->font && c->var_store)
delta = roundf (get_delta (c->font, *c->var_store, c->store_cache));
/* set new varidx to HB_OT_LAYOUT_NO_VARIATIONS_INDEX here, will remap
* varidx later*/
c->varidx_delta_map->set (varIdx, hb_pair_t<unsigned, int> (HB_OT_LAYOUT_NO_VARIATIONS_INDEX, delta));
}
bool sanitize (hb_sanitize_context_t *c) const
@ -3282,7 +3288,8 @@ struct Device
}
}
Device* copy (hb_serialize_context_t *c, const hb_map_t *layout_variation_idx_map=nullptr) const
Device* copy (hb_serialize_context_t *c,
const hb_hashmap_t<unsigned, hb_pair_t<unsigned, int>> *layout_variation_idx_delta_map=nullptr) const
{
TRACE_SERIALIZE (this);
switch (u.b.format) {
@ -3294,14 +3301,14 @@ struct Device
#endif
#ifndef HB_NO_VAR
case 0x8000:
return_trace (reinterpret_cast<Device *> (u.variation.copy (c, layout_variation_idx_map)));
return_trace (reinterpret_cast<Device *> (u.variation.copy (c, layout_variation_idx_delta_map)));
#endif
default:
return_trace (nullptr);
}
}
void collect_variation_indices (hb_set_t *layout_variation_indices) const
void collect_variation_indices (hb_collect_variation_indices_context_t *c) const
{
switch (u.b.format) {
#ifndef HB_NO_HINTING
@ -3312,7 +3319,7 @@ struct Device
#endif
#ifndef HB_NO_VAR
case 0x8000:
u.variation.record_variation_index (layout_variation_indices);
u.variation.collect_variation_index (c);
return;
#endif
default:
@ -3320,6 +3327,18 @@ struct Device
}
}
unsigned get_variation_index () const
{
switch (u.b.format) {
#ifndef HB_NO_VAR
case 0x8000:
return u.variation.varIdx;
#endif
default:
return HB_OT_LAYOUT_NO_VARIATIONS_INDEX;
}
}
protected:
union {
DeviceHeader b;

View File

@ -200,15 +200,34 @@ struct CaretValueFormat3
bool subset (hb_subset_context_t *c) const
{
TRACE_SUBSET (this);
auto *out = c->serializer->embed (this);
auto *out = c->serializer->start_embed (*this);
if (unlikely (!out)) return_trace (false);
if (!c->serializer->embed (caretValueFormat)) return_trace (false);
if (!c->serializer->embed (coordinate)) return_trace (false);
return_trace (out->deviceTable.serialize_copy (c->serializer, deviceTable, this, c->serializer->to_bias (out),
hb_serialize_context_t::Head, c->plan->layout_variation_idx_map));
unsigned varidx = (this+deviceTable).get_variation_index ();
if (c->plan->layout_variation_idx_delta_map->has (varidx))
{
int delta = hb_second (c->plan->layout_variation_idx_delta_map->get (varidx));
if (delta != 0)
{
if (!c->serializer->check_assign (out->coordinate, coordinate + delta, HB_SERIALIZE_ERROR_INT_OVERFLOW))
return_trace (false);
}
}
void collect_variation_indices (hb_set_t *layout_variation_indices) const
{ (this+deviceTable).collect_variation_indices (layout_variation_indices); }
if (c->plan->all_axes_pinned)
return_trace (c->serializer->check_assign (out->caretValueFormat, 1, HB_SERIALIZE_ERROR_INT_OVERFLOW));
if (!c->serializer->embed (deviceTable))
return_trace (false);
return_trace (out->deviceTable.serialize_copy (c->serializer, deviceTable, this, c->serializer->to_bias (out),
hb_serialize_context_t::Head, c->plan->layout_variation_idx_delta_map));
}
void collect_variation_indices (hb_collect_variation_indices_context_t *c) const
{ (this+deviceTable).collect_variation_indices (c); }
bool sanitize (hb_sanitize_context_t *c) const
{
@ -255,14 +274,14 @@ struct CaretValue
}
}
void collect_variation_indices (hb_set_t *layout_variation_indices) const
void collect_variation_indices (hb_collect_variation_indices_context_t *c) const
{
switch (u.format) {
case 1:
case 2:
return;
case 3:
u.format3.collect_variation_indices (layout_variation_indices);
u.format3.collect_variation_indices (c);
return;
default: return;
}
@ -329,7 +348,7 @@ struct LigGlyph
void collect_variation_indices (hb_collect_variation_indices_context_t *c) const
{
for (const Offset16To<CaretValue>& offset : carets.iter ())
(this+offset).collect_variation_indices (c->layout_variation_indices);
(this+offset).collect_variation_indices (c);
}
bool sanitize (hb_sanitize_context_t *c) const
@ -586,7 +605,10 @@ struct GDEFVersion1_2
bool subset_varstore = false;
if (version.to_int () >= 0x00010003u)
{
subset_varstore = out->varStore.serialize_subset (c, varStore, this);
if (c->plan->all_axes_pinned)
out->varStore = 0;
else
subset_varstore = out->varStore.serialize_subset (c, varStore, this, c->plan->gdef_varstore_inner_maps.as_array ());
}
if (subset_varstore)
@ -846,7 +868,7 @@ struct GDEF
{ get_lig_caret_list ().collect_variation_indices (c); }
void remap_layout_variation_indices (const hb_set_t *layout_variation_indices,
hb_map_t *layout_variation_idx_map /* OUT */) const
hb_hashmap_t<unsigned, hb_pair_t<unsigned, int>> *layout_variation_idx_delta_map /* OUT */) const
{
if (!has_var_store ()) return;
if (layout_variation_indices->is_empty ()) return;
@ -864,7 +886,11 @@ struct GDEF
}
unsigned new_idx = (new_major << 16) + new_minor;
layout_variation_idx_map->set (idx, new_idx);
if (!layout_variation_idx_delta_map->has (idx))
continue;
int delta = hb_second (layout_variation_idx_delta_map->get (idx));
layout_variation_idx_delta_map->set (idx, hb_pair_t<unsigned, int> (new_idx, delta));
++new_minor;
last_major = major;
}

View File

@ -175,10 +175,36 @@ struct OS2
int a = (int) floorf (ratio);
int b = (int) ceilf (ratio);
/* follow this maping:
* https://docs.microsoft.com/en-us/typography/opentype/spec/os2#uswidthclass
*/
if (b <= 6) // 50-125
{
if (a == b) return a + 1.0f;
}
else if (b == 7) // no mapping for 137.5
{
a = 6;
b = 8;
}
else if (b == 8)
{
if (a == b) return 8.0f; // 150
a = 6;
}
else
{
if (a == b && a == 12) return 9.0f; //200
b = 12;
a = 8;
}
float va = 50 + a * 12.5f;
float vb = 50 + b * 12.5f;
return a + 1.0f + (float) (b - a) * (width - va) / (vb - va);
float ret = a + (width - va) / (vb - va);
if (a <= 6) ret += 1.0f;
return ret;
}
bool subset (hb_subset_context_t *c) const

View File

@ -269,11 +269,46 @@ _closure_glyphs_lookups_features (hb_subset_plan_t *plan,
#ifndef HB_NO_VAR
static inline void
_collect_layout_variation_indices (hb_subset_plan_t* plan,
const hb_set_t *glyphset,
const hb_map_t *gpos_lookups,
hb_set_t *layout_variation_indices,
hb_map_t *layout_variation_idx_map)
_generate_varstore_inner_maps (const hb_set_t& varidx_set,
unsigned subtable_count,
hb_vector_t<hb_inc_bimap_t> &inner_maps /* OUT */)
{
if (varidx_set.is_empty () || subtable_count == 0) return;
inner_maps.resize (subtable_count);
for (unsigned idx : varidx_set)
{
uint16_t major = idx >> 16;
uint16_t minor = idx & 0xFFFF;
if (major >= subtable_count)
continue;
inner_maps[major].add (minor);
}
}
static inline hb_font_t*
_get_hb_font_with_variations (const hb_subset_plan_t *plan)
{
hb_font_t *font = hb_font_create (plan->source);
hb_vector_t<hb_variation_t> vars;
vars.alloc (plan->user_axes_location->get_population ());
for (auto _ : *plan->user_axes_location)
{
hb_variation_t var;
var.tag = _.first;
var.value = _.second;
vars.push (var);
}
hb_font_set_variations (font, vars.arrayZ, plan->user_axes_location->get_population ());
return font;
}
static inline void
_collect_layout_variation_indices (hb_subset_plan_t* plan)
{
hb_blob_ptr_t<OT::GDEF> gdef = plan->source_table<OT::GDEF> ();
hb_blob_ptr_t<GPOS> gpos = plan->source_table<GPOS> ();
@ -284,13 +319,40 @@ _collect_layout_variation_indices (hb_subset_plan_t* plan,
gpos.destroy ();
return;
}
OT::hb_collect_variation_indices_context_t c (layout_variation_indices, glyphset, gpos_lookups);
const OT::VariationStore *var_store = nullptr;
hb_set_t varidx_set;
hb_font_t *font = nullptr;
float *store_cache = nullptr;
bool collect_delta = plan->pinned_at_default ? false : true;
if (collect_delta)
{
font = _get_hb_font_with_variations (plan);
if (gdef->has_var_store ())
{
var_store = &(gdef->get_var_store ());
store_cache = var_store->create_cache ();
}
}
OT::hb_collect_variation_indices_context_t c (&varidx_set,
plan->layout_variation_idx_delta_map,
font, var_store,
plan->_glyphset_gsub,
plan->gpos_lookups,
store_cache);
gdef->collect_variation_indices (&c);
if (hb_ot_layout_has_positioning (plan->source))
gpos->collect_variation_indices (&c);
gdef->remap_layout_variation_indices (layout_variation_indices, layout_variation_idx_map);
hb_font_destroy (font);
var_store->destroy_cache (store_cache);
gdef->remap_layout_variation_indices (&varidx_set, plan->layout_variation_idx_delta_map);
unsigned subtable_count = gdef->has_var_store () ? gdef->get_var_store ().get_sub_table_count () : 0;
_generate_varstore_inner_maps (varidx_set, subtable_count, plan->gdef_varstore_inner_maps);
gdef.destroy ();
gpos.destroy ();
@ -506,11 +568,7 @@ _populate_gids_to_retain (hb_subset_plan_t* plan,
#ifndef HB_NO_VAR
if (close_over_gdef)
_collect_layout_variation_indices (plan,
plan->_glyphset_gsub,
plan->gpos_lookups,
plan->layout_variation_indices,
plan->layout_variation_idx_map);
_collect_layout_variation_indices (plan);
#endif
}
@ -682,8 +740,8 @@ hb_subset_plan_create_or_fail (hb_face_t *face,
plan->gpos_features = hb_map_create ();
plan->colrv1_layers = hb_map_create ();
plan->colr_palettes = hb_map_create ();
plan->layout_variation_indices = hb_set_create ();
plan->layout_variation_idx_map = hb_map_create ();
plan->check_success (plan->layout_variation_idx_delta_map = hb_hashmap_create<unsigned, hb_pair_t<unsigned, int>> ());
plan->gdef_varstore_inner_maps.init ();
plan->check_success (plan->sanitized_table_cache = hb_hashmap_create<hb_tag_t, hb::unique_ptr<hb_blob_t>> ());
plan->check_success (plan->axes_location = hb_hashmap_create<hb_tag_t, int> ());
@ -701,6 +759,10 @@ hb_subset_plan_create_or_fail (hb_face_t *face,
return nullptr;
}
#ifndef HB_NO_VAR
_normalize_axes_location (face, plan);
#endif
_populate_unicodes_to_retain (input->sets.unicodes, input->sets.glyphs, plan);
_populate_gids_to_retain (plan,
@ -728,10 +790,6 @@ hb_subset_plan_create_or_fail (hb_face_t *face,
plan->glyph_map->get(plan->unicode_to_new_gid_list.arrayZ[i].second);
}
#ifndef HB_NO_VAR
_normalize_axes_location (face, plan);
#endif
_nameid_closure (face, plan->name_ids, plan->all_axes_pinned, plan->user_axes_location);
if (unlikely (plan->in_error ())) {
hb_subset_plan_destroy (plan);

View File

@ -33,6 +33,7 @@
#include "hb-subset-input.hh"
#include "hb-map.hh"
#include "hb-bimap.hh"
#include "hb-set.hh"
struct hb_subset_plan_t
@ -66,8 +67,6 @@ struct hb_subset_plan_t
hb_map_destroy (gpos_features);
hb_map_destroy (colrv1_layers);
hb_map_destroy (colr_palettes);
hb_set_destroy (layout_variation_indices);
hb_map_destroy (layout_variation_idx_map);
hb_hashmap_destroy (gsub_langsys);
hb_hashmap_destroy (gpos_langsys);
@ -75,6 +74,7 @@ struct hb_subset_plan_t
hb_hashmap_destroy (sanitized_table_cache);
hb_hashmap_destroy (hmtx_map);
hb_hashmap_destroy (vmtx_map);
hb_hashmap_destroy (layout_variation_idx_delta_map);
if (user_axes_location)
{
@ -147,10 +147,11 @@ struct hb_subset_plan_t
hb_map_t *colrv1_layers;
hb_map_t *colr_palettes;
//The set of layout item variation store delta set indices to be retained
hb_set_t *layout_variation_indices;
//Old -> New layout item variation store delta set index mapping
hb_map_t *layout_variation_idx_map;
//Old layout item variation index -> (New varidx, delta) mapping
hb_hashmap_t<unsigned, hb_pair_t<unsigned, int>> *layout_variation_idx_delta_map;
//gdef varstore retained varidx mapping
hb_vector_t<hb_inc_bimap_t> gdef_varstore_inner_maps;
hb_hashmap_t<hb_tag_t, hb::unique_ptr<hb_blob_t>>* sanitized_table_cache;
//normalized axes location map

View File

@ -0,0 +1,12 @@
FONTS:
Roboto-Variable.ttf
PROFILES:
default.txt
SUBSETS:
*
INSTANCES:
wght=150,wdth=80
wght=300,wdth=90

View File

@ -34,9 +34,6 @@ def generate_expected_output(input_file, unicodes, profile_flags, instance_flags
"--drop-tables-=sbix",
"--unicodes=%s" % unicodes,
"--output-file=%s" % fonttools_path])
#TODO: remove the drop later as instancing support is added to GPOS/GDEF.
if instance_flags:
args.extend(["--drop-tables+=GPOS,GDEF"])
args.extend(profile_flags)
check_call(args)
@ -66,10 +63,8 @@ def generate_expected_output(input_file, unicodes, profile_flags, instance_flags
"--drop-tables+=DSIG",
"--drop-tables-=sbix"]
args.extend(profile_flags)
#TODO: remove the drop later as instancing support is added to GPOS/GDEF.
if instance_flags:
args.extend(["--drop-tables+=GDEF,GPOS",
"--instance=%s" % ','.join(instance_flags)])
args.extend(["--instance=%s" % ','.join(instance_flags)])
check_call(args)
with io.StringIO () as fp:

View File

@ -53,6 +53,7 @@ tests = [
# instacing tests, enable when --instance is not experimental
# 'pin_all_at_default',
# 'instantiate_glyf',
# 'full_instance',
]
repack_tests = [

View File

@ -56,8 +56,7 @@ def run_test (test, should_check_ots):
"--drop-tables-=sbix"]
cli_args.extend (test.get_profile_flags ())
if test.get_instance_flags ():
cli_args.extend (["--drop-tables+=GPOS,GDEF",
"--instance=%s" % ','.join(test.get_instance_flags ())])
cli_args.extend (["--instance=%s" % ','.join(test.get_instance_flags ())])
ret = subset_cmd (cli_args)
if ret != "success":