HarfBuzz: Update to version 5.3.1

This commit is contained in:
bruvzg 2022-10-21 10:36:19 +03:00
parent 72b845b287
commit 7afd76bba6
No known key found for this signature in database
GPG key ID: 7960FCF39844EC38
46 changed files with 1349 additions and 443 deletions

View file

@ -214,7 +214,7 @@ Files extracted from upstream source:
## harfbuzz
- Upstream: https://github.com/harfbuzz/harfbuzz
- Version: 5.2.0 (4a1d891c6317d2c83e5f3c2607ec5f5ccedffcde, 2022)
- Version: 5.3.1 (970321db7bddbe8c579b73751fc655a924ea3ce6, 2022)
- License: MIT
Files extracted from upstream source:

View file

@ -39,7 +39,7 @@ struct GPOS : GSUBGPOS
bool subset (hb_subset_context_t *c) const
{
hb_subset_layout_context_t l (c, tableTag, c->plan->gpos_lookups, c->plan->gpos_langsys, c->plan->gpos_features);
hb_subset_layout_context_t l (c, tableTag);
return GSUBGPOS::subset<PosLookup> (&l);
}

View file

@ -80,6 +80,24 @@ struct SinglePosFormat1
return_trace (true);
}
bool
position_single (hb_font_t *font,
hb_direction_t direction,
hb_codepoint_t gid,
hb_glyph_position_t &pos) const
{
unsigned int index = (this+coverage).get_coverage (gid);
if (likely (index == NOT_COVERED)) return false;
/* This is ugly... */
hb_buffer_t buffer;
buffer.props.direction = direction;
OT::hb_ot_apply_context_t c (1, font, &buffer);
valueFormat.apply_value (&c, this, values, pos);
return true;
}
template<typename Iterator,
typename SrcLookup,
hb_requires (hb_is_iterator (Iterator))>

View file

@ -68,7 +68,7 @@ struct SinglePosFormat2
unsigned int index = (this+coverage).get_coverage (buffer->cur().codepoint);
if (likely (index == NOT_COVERED)) return_trace (false);
if (likely (index >= valueCount)) return_trace (false);
if (unlikely (index >= valueCount)) return_trace (false);
if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
{
@ -92,6 +92,28 @@ struct SinglePosFormat2
return_trace (true);
}
bool
position_single (hb_font_t *font,
hb_direction_t direction,
hb_codepoint_t gid,
hb_glyph_position_t &pos) const
{
unsigned int index = (this+coverage).get_coverage (gid);
if (likely (index == NOT_COVERED)) return false;
if (unlikely (index >= valueCount)) return false;
/* This is ugly... */
hb_buffer_t buffer;
buffer.props.direction = direction;
OT::hb_ot_apply_context_t c (1, font, &buffer);
valueFormat.apply_value (&c, this,
&values[index * valueFormat.get_len ()],
pos);
return true;
}
template<typename Iterator,
typename SrcLookup,
hb_requires (hb_is_iterator (Iterator))>

View file

@ -27,7 +27,7 @@ struct GSUB : GSUBGPOS
bool subset (hb_subset_context_t *c) const
{
hb_subset_layout_context_t l (c, tableTag, c->plan->gsub_lookups, c->plan->gsub_langsys, c->plan->gsub_features);
hb_subset_layout_context_t l (c, tableTag);
return GSUBGPOS::subset<SubstLookup> (&l);
}

View file

@ -118,7 +118,7 @@ struct Ligature
match_positions[i] += delta;
if (i)
*p++ = ',';
sprintf (p, "%u", match_positions[i]);
snprintf (p, sizeof(buf), "%u", match_positions[i]);
p += strlen(p);
}

View file

@ -117,7 +117,7 @@ struct Sequence
{
if (buf < p)
*p++ = ',';
sprintf (p, "%u", i);
snprintf (p, sizeof(buf), "%u", i);
p += strlen(p);
}

View file

@ -102,17 +102,19 @@ struct Glyph
hb_bytes_t &dest_bytes /* OUT */) const
{
GlyphHeader *glyph_header = nullptr;
if (all_points.length > 4)
if (type != EMPTY && all_points.length > 4)
{
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);
int yMin, yMax;
yMin = yMax = roundf (all_points[0].y);
int xMin = 0, xMax = 0;
int yMin = 0, yMax = 0;
if (all_points.length > 4)
{
xMin = xMax = roundf (all_points[0].x);
yMin = yMax = roundf (all_points[0].y);
}
for (unsigned i = 1; i < all_points.length - 4; i++)
{
@ -128,7 +130,7 @@ struct Glyph
/*for empty glyphs: all_points only include phantom points.
*just update metrics and then return */
if (all_points.length == 4)
if (!glyph_header)
return true;
glyph_header->numberOfContours = header->numberOfContours;
@ -145,10 +147,17 @@ struct Glyph
hb_font_t *font,
const glyf_accelerator_t &glyf,
hb_bytes_t &dest_start, /* IN/OUT */
hb_bytes_t &dest_end /* OUT */) const
hb_bytes_t &dest_end /* OUT */)
{
contour_point_vector_t all_points, deltas;
get_points (font, glyf, all_points, &deltas, false);
if (!get_points (font, glyf, all_points, &deltas, false, false))
return false;
// .notdef, set type to empty so we only update metrics and don't compile bytes for
// it
if (gid == 0 &&
!(plan->flags & HB_SUBSET_FLAGS_NOTDEF_OUTLINE))
type = EMPTY;
switch (type) {
case COMPOSITE:
@ -171,7 +180,12 @@ struct Glyph
break;
}
return compile_header_bytes (plan, all_points, dest_start);
if (!compile_header_bytes (plan, all_points, dest_start))
{
dest_end.fini ();
return false;
}
return true;
}
@ -182,6 +196,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 shift_points_hori = true,
bool use_my_metrics = true,
bool phantom_only = false,
unsigned int depth = 0) const
@ -238,8 +253,7 @@ struct Glyph
if (deltas != nullptr && depth == 0 && type == COMPOSITE)
{
if (unlikely (!deltas->resize (points.length))) return false;
for (unsigned i = 0 ; i < points.length; i++)
deltas->arrayZ[i] = points.arrayZ[i];
deltas->copy_vector (points);
}
#ifndef HB_NO_VAR
@ -271,14 +285,9 @@ struct Glyph
comp_points.reset ();
if (unlikely (!glyf_accelerator.glyph_for_gid (item.get_gid ())
.get_points (font, glyf_accelerator, comp_points,
deltas, use_my_metrics, phantom_only, depth + 1)))
deltas, shift_points_hori, use_my_metrics, phantom_only, depth + 1)))
return false;
/* Copy phantom points from component if USE_MY_METRICS flag set */
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];
/* Apply component transformation & translation */
item.transform_points (comp_points);
@ -299,6 +308,11 @@ struct Glyph
}
}
/* Copy phantom points from component if USE_MY_METRICS flag set */
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];
all_points.extend (comp_points.sub_array (0, comp_points.length - PHANTOM_COUNT));
comp_index++;
@ -310,7 +324,7 @@ struct Glyph
all_points.extend (phantoms);
}
if (depth == 0) /* Apply at top level */
if (depth == 0 && shift_points_hori) /* Apply at top level */
{
/* Undocumented rasterizer behavior:
* Shift points horizontally by the updated left side bearing
@ -332,10 +346,16 @@ struct Glyph
hb_bytes_t get_bytes () const { return bytes; }
Glyph (hb_bytes_t bytes_ = hb_bytes_t (),
hb_codepoint_t gid_ = (hb_codepoint_t) -1) : bytes (bytes_),
header (bytes.as<GlyphHeader> ()),
gid (gid_)
Glyph () : bytes (),
header (bytes.as<GlyphHeader> ()),
gid (-1),
type(EMPTY)
{}
Glyph (hb_bytes_t bytes_,
hb_codepoint_t gid_ = (unsigned) -1) : bytes (bytes_),
header (bytes.as<GlyphHeader> ()),
gid (gid_)
{
int num_contours = header->numberOfContours;
if (unlikely (num_contours == 0)) type = EMPTY;

View file

@ -271,31 +271,29 @@ struct SimpleGlyph
}
//convert absolute values to relative values
unsigned num_points = all_points.length - 4;
hb_vector_t<hb_pair_t<int, int>> deltas;
deltas.resize (num_points);
for (unsigned i = 0; i < num_points; i++)
{
deltas[i].first = i == 0 ? roundf (all_points[i].x) : roundf (all_points[i].x) - roundf (all_points[i-1].x);
deltas[i].second = i == 0 ? roundf (all_points[i].y) : roundf (all_points[i].y) - roundf (all_points[i-1].y);
}
hb_vector_t<uint8_t> flags, x_coords, y_coords;
flags.alloc (num_points);
x_coords.alloc (2*num_points);
y_coords.alloc (2*num_points);
if (unlikely (!flags.alloc (num_points))) return false;
if (unlikely (!x_coords.alloc (2*num_points))) return false;
if (unlikely (!y_coords.alloc (2*num_points))) return false;
uint8_t lastflag = 0, repeat = 0;
int prev_x = 0.f, prev_y = 0.f;
for (unsigned i = 0; i < num_points; i++)
{
uint8_t flag = all_points[i].flag;
flag &= FLAG_ON_CURVE + FLAG_OVERLAP_SIMPLE;
encode_coord (deltas[i].first, flag, FLAG_X_SHORT, FLAG_X_SAME, x_coords);
encode_coord (deltas[i].second, flag, FLAG_Y_SHORT, FLAG_Y_SAME, y_coords);
float cur_x = roundf (all_points[i].x);
float cur_y = roundf (all_points[i].y);
encode_coord (cur_x - prev_x, flag, FLAG_X_SHORT, FLAG_X_SAME, x_coords);
encode_coord (cur_y - prev_y, flag, FLAG_Y_SHORT, FLAG_Y_SAME, y_coords);
if (i == 0) lastflag = flag + 1; //make lastflag != flag for the first point
encode_flag (flag, repeat, lastflag, flags);
prev_x = cur_x;
prev_y = cur_y;
}
unsigned len_before_instrs = 2 * header.numberOfContours + 2;

View file

@ -14,7 +14,6 @@ namespace glyf_impl {
struct SubsetGlyph
{
hb_codepoint_t new_gid;
hb_codepoint_t old_gid;
Glyph source_glyph;
hb_bytes_t dest_start; /* region of source_glyph to copy first */

View file

@ -72,10 +72,13 @@ struct glyf
if (unlikely (!c->serializer->check_success (glyf_prime))) return_trace (false);
hb_vector_t<glyf_impl::SubsetGlyph> glyphs;
_populate_subset_glyphs (c->plan, &glyphs);
_populate_subset_glyphs (c->plan, glyphs);
if (!c->plan->pinned_at_default)
_compile_subset_glyphs_with_deltas (c->plan, &glyphs);
{
if (!_compile_subset_glyphs_with_deltas (c->plan, &glyphs))
return_trace (false);
}
auto padded_offsets =
+ hb_iter (glyphs)
@ -105,9 +108,9 @@ struct glyf
void
_populate_subset_glyphs (const hb_subset_plan_t *plan,
hb_vector_t<glyf_impl::SubsetGlyph> *glyphs /* OUT */) const;
void
hb_vector_t<glyf_impl::SubsetGlyph> &glyphs /* OUT */) const;
bool
_compile_subset_glyphs_with_deltas (const hb_subset_plan_t *plan,
hb_vector_t<glyf_impl::SubsetGlyph> *glyphs /* OUT */) const;
@ -180,7 +183,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, true, phantom_only)))
if (unlikely (!glyph_for_gid (gid).get_points (font, *this, all_points, nullptr, true, true, phantom_only)))
return false;
if (consumer.is_consuming_contour_points ())
@ -374,44 +377,43 @@ struct glyf_accelerator_t
inline void
glyf::_populate_subset_glyphs (const hb_subset_plan_t *plan,
hb_vector_t<glyf_impl::SubsetGlyph> *glyphs /* OUT */) const
hb_vector_t<glyf_impl::SubsetGlyph>& glyphs /* OUT */) const
{
OT::glyf_accelerator_t glyf (plan->source);
unsigned num_glyphs = plan->num_output_glyphs ();
if (!glyphs.resize (num_glyphs)) return;
+ hb_range (plan->num_output_glyphs ())
| hb_map ([&] (hb_codepoint_t new_gid)
{
glyf_impl::SubsetGlyph subset_glyph = {0};
subset_glyph.new_gid = new_gid;
for (auto p : plan->glyph_map->iter ())
{
unsigned new_gid = p.second;
glyf_impl::SubsetGlyph& subset_glyph = glyphs.arrayZ[new_gid];
subset_glyph.old_gid = p.first;
/* should never fail: all old gids should be mapped */
if (!plan->old_gid_for_new_gid (new_gid, &subset_glyph.old_gid))
return subset_glyph;
if (unlikely (new_gid == 0 &&
!(plan->flags & HB_SUBSET_FLAGS_NOTDEF_OUTLINE)) &&
plan->pinned_at_default)
subset_glyph.source_glyph = glyf_impl::Glyph ();
else
subset_glyph.source_glyph = glyf.glyph_for_gid (subset_glyph.old_gid, true);
if (new_gid == 0 &&
!(plan->flags & HB_SUBSET_FLAGS_NOTDEF_OUTLINE))
subset_glyph.source_glyph = glyf_impl::Glyph ();
else
subset_glyph.source_glyph = glyf.glyph_for_gid (subset_glyph.old_gid, true);
if (plan->flags & HB_SUBSET_FLAGS_NO_HINTING)
subset_glyph.drop_hints_bytes ();
else
subset_glyph.dest_start = subset_glyph.source_glyph.get_bytes ();
return subset_glyph;
})
| hb_sink (glyphs)
;
if (plan->flags & HB_SUBSET_FLAGS_NO_HINTING)
subset_glyph.drop_hints_bytes ();
else
subset_glyph.dest_start = subset_glyph.source_glyph.get_bytes ();
}
}
inline void
inline bool
glyf::_compile_subset_glyphs_with_deltas (const hb_subset_plan_t *plan,
hb_vector_t<glyf_impl::SubsetGlyph> *glyphs /* OUT */) const
{
OT::glyf_accelerator_t glyf (plan->source);
hb_font_t *font = hb_font_create (plan->source);
if (unlikely (!font)) return false;
hb_vector_t<hb_variation_t> vars;
vars.alloc (plan->user_axes_location->get_population ());
if (unlikely (!vars.alloc (plan->user_axes_location->get_population ())))
return false;
for (auto _ : *plan->user_axes_location)
{
@ -423,9 +425,16 @@ glyf::_compile_subset_glyphs_with_deltas (const hb_subset_plan_t *plan,
hb_font_set_variations (font, vars.arrayZ, plan->user_axes_location->get_population ());
for (auto& subset_glyph : *glyphs)
const_cast<glyf_impl::SubsetGlyph &> (subset_glyph).compile_bytes_with_deltas (plan, font, glyf);
{
if (!const_cast<glyf_impl::SubsetGlyph &> (subset_glyph).compile_bytes_with_deltas (plan, font, glyf))
{
hb_font_destroy (font);
return false;
}
}
hb_font_destroy (font);
return true;
}

View file

@ -70,8 +70,8 @@ struct graph_t
{
DEBUG_MSG (SUBSET_REPACK, nullptr,
"vertex [%lu] bytes != [%lu] bytes, depth = %u",
table_size (),
other.table_size (),
(unsigned long) table_size (),
(unsigned long) other.table_size (),
depth);
auto a = as_bytes ();
@ -495,6 +495,12 @@ struct graph_t
return as_table_from_index<T> (index_for_offset (parent, offset), std::forward<Ts>(ds)...);
}
template <typename T, typename ...Ts>
vertex_and_table_t<T> as_mutable_table (unsigned parent, const void* offset, Ts... ds)
{
return as_table_from_index<T> (mutable_index_for_offset (parent, offset), std::forward<Ts>(ds)...);
}
template <typename T, typename ...Ts>
vertex_and_table_t<T> as_table_from_index (unsigned index, Ts... ds)
{
@ -833,7 +839,20 @@ struct graph_t
* parent to the clone. The copy is a shallow copy, objects
* linked from child are not duplicated.
*/
bool duplicate (unsigned parent_idx, unsigned child_idx)
unsigned duplicate_if_shared (unsigned parent_idx, unsigned child_idx)
{
unsigned new_idx = duplicate (parent_idx, child_idx);
if (new_idx == (unsigned) -1) return child_idx;
return new_idx;
}
/*
* Creates a copy of child and re-assigns the link from
* parent to the clone. The copy is a shallow copy, objects
* linked from child are not duplicated.
*/
unsigned duplicate (unsigned parent_idx, unsigned child_idx)
{
update_parents ();
@ -849,7 +868,7 @@ struct graph_t
// to child are from parent.
DEBUG_MSG (SUBSET_REPACK, nullptr, " Not duplicating %d => %d",
parent_idx, child_idx);
return false;
return -1;
}
DEBUG_MSG (SUBSET_REPACK, nullptr, " Duplicating %d => %d",
@ -869,7 +888,7 @@ struct graph_t
reassign_link (l, parent_idx, clone_idx);
}
return true;
return clone_idx;
}

View file

@ -131,8 +131,10 @@ struct Lookup : public OT::Lookup
for (unsigned i = 0; i < subTable.len; i++)
{
unsigned subtable_index = c.graph.index_for_offset (this_index, &subTable[i]);
unsigned parent_index = this_index;
if (is_ext) {
unsigned ext_subtable_index = subtable_index;
parent_index = ext_subtable_index;
ExtensionFormat1<OT::Layout::GSUB_impl::ExtensionSubst>* extension =
(ExtensionFormat1<OT::Layout::GSUB_impl::ExtensionSubst>*)
c.graph.object (ext_subtable_index).head;
@ -150,9 +152,9 @@ struct Lookup : public OT::Lookup
switch (type)
{
case 2:
new_sub_tables = split_subtable<PairPos> (c, subtable_index); break;
new_sub_tables = split_subtable<PairPos> (c, parent_index, subtable_index); break;
case 4:
new_sub_tables = split_subtable<MarkBasePos> (c, subtable_index); break;
new_sub_tables = split_subtable<MarkBasePos> (c, parent_index, subtable_index); break;
default:
break;
}
@ -172,13 +174,14 @@ struct Lookup : public OT::Lookup
template<typename T>
hb_vector_t<unsigned> split_subtable (gsubgpos_graph_context_t& c,
unsigned parent_idx,
unsigned objidx)
{
T* sub_table = (T*) c.graph.object (objidx).head;
if (!sub_table || !sub_table->sanitize (c.graph.vertices_[objidx]))
return hb_vector_t<unsigned> ();
return sub_table->split_subtables (c, objidx);
return sub_table->split_subtables (c, parent_idx, objidx);
}
void add_sub_tables (gsubgpos_graph_context_t& c,

View file

@ -209,7 +209,9 @@ struct MarkBasePosFormat1 : public OT::Layout::GPOS_impl::MarkBasePosFormat1_2<S
return vertex_len >= MarkBasePosFormat1::static_size;
}
hb_vector_t<unsigned> split_subtables (gsubgpos_graph_context_t& c, unsigned this_index)
hb_vector_t<unsigned> split_subtables (gsubgpos_graph_context_t& c,
unsigned parent_index,
unsigned this_index)
{
hb_set_t visited;
@ -261,7 +263,7 @@ struct MarkBasePosFormat1 : public OT::Layout::GPOS_impl::MarkBasePosFormat1_2<S
split_context_t split_context {
c,
this,
this_index,
c.graph.duplicate_if_shared (parent_index, this_index),
std::move (class_to_info),
c.graph.vertices_[mark_array_id].position_to_index_map (),
};
@ -365,8 +367,8 @@ struct MarkBasePosFormat1 : public OT::Layout::GPOS_impl::MarkBasePosFormat1_2<S
classCount = count;
auto mark_coverage = sc.c.graph.as_table<Coverage> (this_index,
&markCoverage);
auto mark_coverage = sc.c.graph.as_mutable_table<Coverage> (this_index,
&markCoverage);
if (!mark_coverage) return false;
hb_set_t marks = sc.marks_for (0, count);
auto new_coverage =
@ -380,17 +382,17 @@ struct MarkBasePosFormat1 : public OT::Layout::GPOS_impl::MarkBasePosFormat1_2<S
return false;
auto base_array = sc.c.graph.as_table<AnchorMatrix> (this_index,
&baseArray,
old_count);
auto base_array = sc.c.graph.as_mutable_table<AnchorMatrix> (this_index,
&baseArray,
old_count);
if (!base_array || !base_array.table->shrink (sc.c,
base_array.index,
old_count,
count))
return false;
auto mark_array = sc.c.graph.as_table<MarkArray> (this_index,
&markArray);
auto mark_array = sc.c.graph.as_mutable_table<MarkArray> (this_index,
&markArray);
if (!mark_array || !mark_array.table->shrink (sc.c,
sc.mark_array_links,
mark_array.index,
@ -469,11 +471,12 @@ struct MarkBasePosFormat1 : public OT::Layout::GPOS_impl::MarkBasePosFormat1_2<S
struct MarkBasePos : public OT::Layout::GPOS_impl::MarkBasePos
{
hb_vector_t<unsigned> split_subtables (gsubgpos_graph_context_t& c,
unsigned parent_index,
unsigned this_index)
{
switch (u.format) {
case 1:
return ((MarkBasePosFormat1*)(&u.format1))->split_subtables (c, this_index);
return ((MarkBasePosFormat1*)(&u.format1))->split_subtables (c, parent_index, this_index);
#ifndef HB_NO_BORING_EXPANSION
case 2: HB_FALLTHROUGH;
// Don't split 24bit PairPos's.

View file

@ -47,7 +47,9 @@ struct PairPosFormat1 : public OT::Layout::GPOS_impl::PairPosFormat1_3<SmallType
min_size + pairSet.get_size () - pairSet.len.get_size();
}
hb_vector_t<unsigned> split_subtables (gsubgpos_graph_context_t& c, unsigned this_index)
hb_vector_t<unsigned> split_subtables (gsubgpos_graph_context_t& c,
unsigned parent_index,
unsigned this_index)
{
hb_set_t visited;
@ -81,7 +83,7 @@ struct PairPosFormat1 : public OT::Layout::GPOS_impl::PairPosFormat1_3<SmallType
split_context_t split_context {
c,
this,
this_index,
c.graph.duplicate_if_shared (parent_index, this_index),
};
return actuate_subtable_split<split_context_t> (split_context, split_points);
@ -125,23 +127,19 @@ struct PairPosFormat1 : public OT::Layout::GPOS_impl::PairPosFormat1_3<SmallType
pairSet.len = count;
c.graph.vertices_[this_index].obj.tail -= (old_count - count) * SmallTypes::size;
unsigned coverage_id = c.graph.mutable_index_for_offset (this_index, &coverage);
unsigned coverage_size = c.graph.vertices_[coverage_id].table_size ();
auto& coverage_v = c.graph.vertices_[coverage_id];
Coverage* coverage_table = (Coverage*) coverage_v.obj.head;
if (!coverage_table || !coverage_table->sanitize (coverage_v))
return false;
auto coverage = c.graph.as_mutable_table<Coverage> (this_index, &this->coverage);
if (!coverage) return false;
unsigned coverage_size = coverage.vertex->table_size ();
auto new_coverage =
+ hb_zip (coverage_table->iter (), hb_range ())
+ hb_zip (coverage.table->iter (), hb_range ())
| hb_filter ([&] (hb_pair_t<unsigned, unsigned> p) {
return p.second < count;
})
| hb_map_retains_sorting (hb_first)
;
return Coverage::make_coverage (c, new_coverage, coverage_id, coverage_size);
return Coverage::make_coverage (c, new_coverage, coverage.index, coverage_size);
}
// Create a new PairPos including PairSet's from start (inclusive) to end (exclusive).
@ -206,7 +204,9 @@ struct PairPosFormat2 : public OT::Layout::GPOS_impl::PairPosFormat2_4<SmallType
min_size + class1_count * get_class1_record_size ();
}
hb_vector_t<unsigned> split_subtables (gsubgpos_graph_context_t& c, unsigned this_index)
hb_vector_t<unsigned> split_subtables (gsubgpos_graph_context_t& c,
unsigned parent_index,
unsigned this_index)
{
const unsigned base_size = OT::Layout::GPOS_impl::PairPosFormat2_4<SmallTypes>::min_size;
const unsigned class_def_2_size = size_of (c, this_index, &classDef2);
@ -287,7 +287,7 @@ struct PairPosFormat2 : public OT::Layout::GPOS_impl::PairPosFormat2_4<SmallType
split_context_t split_context {
c,
this,
this_index,
c.graph.duplicate_if_shared (parent_index, this_index),
class1_record_size,
total_value_len,
value_1_len,
@ -508,40 +508,37 @@ struct PairPosFormat2 : public OT::Layout::GPOS_impl::PairPosFormat2_4<SmallType
graph.vertices_[split_context.this_index].obj.tail -=
(old_count - count) * split_context.class1_record_size;
unsigned coverage_id =
graph.mutable_index_for_offset (split_context.this_index, &coverage);
unsigned class_def_1_id =
graph.mutable_index_for_offset (split_context.this_index, &classDef1);
auto& coverage_v = graph.vertices_[coverage_id];
auto& class_def_1_v = graph.vertices_[class_def_1_id];
Coverage* coverage_table = (Coverage*) coverage_v.obj.head;
ClassDef* class_def_1_table = (ClassDef*) class_def_1_v.obj.head;
if (!coverage_table
|| !coverage_table->sanitize (coverage_v)
|| !class_def_1_table
|| !class_def_1_table->sanitize (class_def_1_v))
return false;
auto coverage =
graph.as_mutable_table<Coverage> (split_context.this_index, &this->coverage);
if (!coverage) return false;
auto class_def_1 =
graph.as_mutable_table<ClassDef> (split_context.this_index, &classDef1);
if (!class_def_1) return false;
auto klass_map =
+ coverage_table->iter ()
+ coverage.table->iter ()
| hb_map_retains_sorting ([&] (hb_codepoint_t gid) {
return hb_pair_t<hb_codepoint_t, hb_codepoint_t> (gid, class_def_1_table->get_class (gid));
return hb_pair_t<hb_codepoint_t, hb_codepoint_t> (gid, class_def_1.table->get_class (gid));
})
| hb_filter ([&] (hb_codepoint_t klass) {
return klass < count;
}, hb_second)
;
auto new_coverage = + klass_map | hb_map_retains_sorting (hb_first);
if (!Coverage::make_coverage (split_context.c,
+ klass_map | hb_map_retains_sorting (hb_first),
coverage_id,
coverage_v.table_size ()))
+ new_coverage,
coverage.index,
// existing ranges my not be kept, worst case size is a format 1
// coverage table.
4 + new_coverage.len() * 2))
return false;
return ClassDef::make_class_def (split_context.c,
+ klass_map,
class_def_1_id,
class_def_1_v.table_size ());
class_def_1.index,
class_def_1.vertex->table_size ());
}
hb_hashmap_t<unsigned, unsigned>
@ -605,13 +602,15 @@ struct PairPosFormat2 : public OT::Layout::GPOS_impl::PairPosFormat2_4<SmallType
struct PairPos : public OT::Layout::GPOS_impl::PairPos
{
hb_vector_t<unsigned> split_subtables (gsubgpos_graph_context_t& c, unsigned this_index)
hb_vector_t<unsigned> split_subtables (gsubgpos_graph_context_t& c,
unsigned parent_index,
unsigned this_index)
{
switch (u.format) {
case 1:
return ((PairPosFormat1*)(&u.format1))->split_subtables (c, this_index);
return ((PairPosFormat1*)(&u.format1))->split_subtables (c, parent_index, this_index);
case 2:
return ((PairPosFormat2*)(&u.format2))->split_subtables (c, this_index);
return ((PairPosFormat2*)(&u.format2))->split_subtables (c, parent_index, this_index);
#ifndef HB_NO_BORING_EXPANSION
case 3: HB_FALLTHROUGH;
case 4: HB_FALLTHROUGH;

View file

@ -70,9 +70,9 @@ struct DecompositionAction
ActionSubrecordHeader
header;
HBFixed lowerLimit; /* If the distance factor is less than this value,
F16DOT16 lowerLimit; /* If the distance factor is less than this value,
* then the ligature is decomposed. */
HBFixed upperLimit; /* If the distance factor is greater than this value,
F16DOT16 upperLimit; /* If the distance factor is greater than this value,
* then the ligature is decomposed. */
HBUINT16 order; /* Numerical order in which this ligature will
* be decomposed; you may want infrequent ligatures
@ -118,7 +118,7 @@ struct ConditionalAddGlyphAction
protected:
ActionSubrecordHeader
header;
HBFixed substThreshold; /* Distance growth factor (in ems) at which
F16DOT16 substThreshold; /* Distance growth factor (in ems) at which
* this glyph is replaced and the growth factor
* recalculated. */
HBGlyphID16 addGlyph; /* Glyph to be added as kashida. If this value is
@ -146,13 +146,13 @@ struct DuctileGlyphAction
HBUINT32 variationAxis; /* The 4-byte tag identifying the ductile axis.
* This would normally be 0x64756374 ('duct'),
* but you may use any axis the font contains. */
HBFixed minimumLimit; /* The lowest value for the ductility axis that
F16DOT16 minimumLimit; /* The lowest value for the ductility axis that
* still yields an acceptable appearance. Normally
* this will be 1.0. */
HBFixed noStretchValue; /* This is the default value that corresponds to
F16DOT16 noStretchValue; /* This is the default value that corresponds to
* no change in appearance. Normally, this will
* be 1.0. */
HBFixed maximumLimit; /* The highest value for the ductility axis that
F16DOT16 maximumLimit; /* The highest value for the ductility axis that
* still yields an acceptable appearance. */
public:
DEFINE_SIZE_STATIC (22);
@ -271,14 +271,14 @@ struct JustWidthDeltaEntry
};
protected:
HBFixed beforeGrowLimit;/* The ratio by which the advance width of the
F16DOT16 beforeGrowLimit;/* The ratio by which the advance width of the
* glyph is permitted to grow on the left or top side. */
HBFixed beforeShrinkLimit;
F16DOT16 beforeShrinkLimit;
/* The ratio by which the advance width of the
* glyph is permitted to shrink on the left or top side. */
HBFixed afterGrowLimit; /* The ratio by which the advance width of the glyph
F16DOT16 afterGrowLimit; /* The ratio by which the advance width of the glyph
* is permitted to shrink on the left or top side. */
HBFixed afterShrinkLimit;
F16DOT16 afterShrinkLimit;
/* The ratio by which the advance width of the glyph
* is at most permitted to shrink on the right or
* bottom side. */

View file

@ -62,7 +62,7 @@ struct TrackTableEntry
}
protected:
HBFixed track; /* Track value for this record. */
F16DOT16 track; /* Track value for this record. */
NameID trackNameID; /* The 'name' table index for this track.
* (a short word or phrase like "loose"
* or "very tight") */
@ -82,7 +82,7 @@ struct TrackData
const void *base) const
{
unsigned int sizes = nSizes;
hb_array_t<const HBFixed> size_table ((base+sizeTable).arrayZ, sizes);
hb_array_t<const F16DOT16> size_table ((base+sizeTable).arrayZ, sizes);
float s0 = size_table[idx].to_float ();
float s1 = size_table[idx + 1].to_float ();
@ -120,7 +120,7 @@ struct TrackData
if (!sizes) return 0.;
if (sizes == 1) return trackTableEntry->get_value (base, 0, sizes);
hb_array_t<const HBFixed> size_table ((base+sizeTable).arrayZ, sizes);
hb_array_t<const F16DOT16> size_table ((base+sizeTable).arrayZ, sizes);
unsigned int size_index;
for (size_index = 0; size_index < sizes - 1; size_index++)
if (size_table[size_index].to_float () >= ptem)
@ -141,7 +141,7 @@ struct TrackData
protected:
HBUINT16 nTracks; /* Number of separate tracks included in this table. */
HBUINT16 nSizes; /* Number of point sizes included in this table. */
NNOffset32To<UnsizedArrayOf<HBFixed>>
NNOffset32To<UnsizedArrayOf<F16DOT16>>
sizeTable; /* Offset from start of the tracking table to
* Array[nSizes] of size values.. */
UnsizedArrayOf<TrackTableEntry>

View file

@ -131,6 +131,7 @@ static const hb_aat_feature_mapping_t feature_mappings[] =
{HB_TAG ('p','n','u','m'), HB_AAT_LAYOUT_FEATURE_TYPE_NUMBER_SPACING, HB_AAT_LAYOUT_FEATURE_SELECTOR_PROPORTIONAL_NUMBERS, (hb_aat_layout_feature_selector_t) 4},
{HB_TAG ('p','w','i','d'), HB_AAT_LAYOUT_FEATURE_TYPE_TEXT_SPACING, HB_AAT_LAYOUT_FEATURE_SELECTOR_PROPORTIONAL_TEXT, (hb_aat_layout_feature_selector_t) 7},
{HB_TAG ('q','w','i','d'), HB_AAT_LAYOUT_FEATURE_TYPE_TEXT_SPACING, HB_AAT_LAYOUT_FEATURE_SELECTOR_QUARTER_WIDTH_TEXT, (hb_aat_layout_feature_selector_t) 7},
{HB_TAG ('r','l','i','g'), HB_AAT_LAYOUT_FEATURE_TYPE_LIGATURES, HB_AAT_LAYOUT_FEATURE_SELECTOR_REQUIRED_LIGATURES_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_REQUIRED_LIGATURES_OFF},
{HB_TAG ('r','u','b','y'), HB_AAT_LAYOUT_FEATURE_TYPE_RUBY_KANA, HB_AAT_LAYOUT_FEATURE_SELECTOR_RUBY_KANA_ON, HB_AAT_LAYOUT_FEATURE_SELECTOR_RUBY_KANA_OFF},
{HB_TAG ('s','i','n','f'), HB_AAT_LAYOUT_FEATURE_TYPE_VERTICAL_POSITION, HB_AAT_LAYOUT_FEATURE_SELECTOR_SCIENTIFIC_INFERIORS, HB_AAT_LAYOUT_FEATURE_SELECTOR_NORMAL_POSITION},
{HB_TAG ('s','m','c','p'), HB_AAT_LAYOUT_FEATURE_TYPE_LOWER_CASE, HB_AAT_LAYOUT_FEATURE_SELECTOR_LOWER_CASE_SMALL_CAPS, HB_AAT_LAYOUT_FEATURE_SELECTOR_DEFAULT_LOWER_CASE},

View file

@ -71,6 +71,7 @@
#define HB_NO_LANGUAGE_PRIVATE_SUBTAG
#define HB_NO_LAYOUT_FEATURE_PARAMS
#define HB_NO_LAYOUT_COLLECT_GLYPHS
#define HB_NO_LAYOUT_RARELY_USED
#define HB_NO_LAYOUT_UNUSED
#define HB_NO_MATH
#define HB_NO_META

View file

@ -69,9 +69,9 @@ struct shared_ptr
operator T * () const { return p; }
T& operator * () const { return *get (); }
T* operator -> () const { return get (); }
operator bool () { return p; }
bool operator == (const shared_ptr &o) { return p == o.p; }
bool operator != (const shared_ptr &o) { return p != o.p; }
operator bool () const { return p; }
bool operator == (const shared_ptr &o) const { return p == o.p; }
bool operator != (const shared_ptr &o) const { return p != o.p; }
static T* get_empty() { return v::get_empty (); }
T* reference() { return v::reference (p); }

View file

@ -633,20 +633,29 @@ hb_face_collect_variation_unicodes (hb_face_t *face,
* face-builder: A face that has add_table().
*/
struct face_table_info_t
{
hb_blob_t* data;
unsigned order;
};
struct hb_face_builder_data_t
{
hb_hashmap_t<hb_tag_t, hb_blob_t *> tables;
hb_hashmap_t<hb_tag_t, face_table_info_t> tables;
};
static int compare_entries (const void* pa, const void* pb)
{
const auto& a = * (const hb_pair_t<hb_tag_t, hb_blob_t*> *) pa;
const auto& b = * (const hb_pair_t<hb_tag_t, hb_blob_t*> *) pb;
const auto& a = * (const hb_pair_t<hb_tag_t, face_table_info_t> *) pa;
const auto& b = * (const hb_pair_t<hb_tag_t, face_table_info_t> *) pb;
/* Order by blob size first (smallest to largest) and then table tag */
if (a.second->length != b.second->length)
return a.second->length < b.second->length ? -1 : +1;
if (a.second.order != b.second.order)
return a.second.order < b.second.order ? -1 : +1;
if (a.second.data->length != b.second.data->length)
return a.second.data->length < b.second.data->length ? -1 : +1;
return a.first < b.first ? -1 : a.first == b.first ? 0 : +1;
}
@ -668,8 +677,8 @@ _hb_face_builder_data_destroy (void *user_data)
{
hb_face_builder_data_t *data = (hb_face_builder_data_t *) user_data;
for (hb_blob_t* b : data->tables.values())
hb_blob_destroy (b);
for (auto info : data->tables.values())
hb_blob_destroy (info.data);
data->tables.fini ();
@ -683,8 +692,8 @@ _hb_face_builder_data_reference_blob (hb_face_builder_data_t *data)
unsigned int table_count = data->tables.get_population ();
unsigned int face_length = table_count * 16 + 12;
for (hb_blob_t* b : data->tables.values())
face_length += hb_ceil_to_4 (hb_blob_get_length (b));
for (auto info : data->tables.values())
face_length += hb_ceil_to_4 (hb_blob_get_length (info.data));
char *buf = (char *) hb_malloc (face_length);
if (unlikely (!buf))
@ -699,7 +708,7 @@ _hb_face_builder_data_reference_blob (hb_face_builder_data_t *data)
hb_tag_t sfnt_tag = is_cff ? OT::OpenTypeFontFile::CFFTag : OT::OpenTypeFontFile::TrueTypeTag;
// Sort the tags so that produced face is deterministic.
hb_vector_t<hb_pair_t <hb_tag_t, hb_blob_t*>> sorted_entries;
hb_vector_t<hb_pair_t <hb_tag_t, face_table_info_t>> sorted_entries;
data->tables.iter () | hb_sink (sorted_entries);
if (unlikely (sorted_entries.in_error ()))
{
@ -708,7 +717,13 @@ _hb_face_builder_data_reference_blob (hb_face_builder_data_t *data)
}
sorted_entries.qsort (compare_entries);
bool ret = f->serialize_single (&c, sfnt_tag, + sorted_entries.iter());
bool ret = f->serialize_single (&c,
sfnt_tag,
+ sorted_entries.iter()
| hb_map ([&] (hb_pair_t<hb_tag_t, face_table_info_t> _) {
return hb_pair_t<hb_tag_t, hb_blob_t*> (_.first, _.second.data);
}));
c.end_serialize ();
@ -729,7 +744,7 @@ _hb_face_builder_reference_table (hb_face_t *face HB_UNUSED, hb_tag_t tag, void
if (!tag)
return _hb_face_builder_data_reference_blob (data);
return hb_blob_reference (data->tables[tag]);
return hb_blob_reference (data->tables[tag].data);
}
@ -777,8 +792,8 @@ hb_face_builder_add_table (hb_face_t *face, hb_tag_t tag, hb_blob_t *blob)
hb_face_builder_data_t *data = (hb_face_builder_data_t *) face->user_data;
hb_blob_t* previous = data->tables.get (tag);
if (!data->tables.set (tag, hb_blob_reference (blob)))
hb_blob_t* previous = data->tables.get (tag).data;
if (!data->tables.set (tag, face_table_info_t {hb_blob_reference (blob), 0}))
{
hb_blob_destroy (blob);
return false;
@ -787,3 +802,36 @@ hb_face_builder_add_table (hb_face_t *face, hb_tag_t tag, hb_blob_t *blob)
hb_blob_destroy (previous);
return true;
}
/**
* hb_face_builder_sort_tables:
* @face: A face object created with hb_face_builder_create()
* @tags: (array zero-terminated=1): ordered list of table tags terminated by
* %HB_TAG_NONE
*
* Set the ordering of tables for serialization. Any tables not
* specified in the tags list will be ordered after the tables in
* tags, ordered by the default sort ordering.
*
* Since: 5.3.0
**/
void
hb_face_builder_sort_tables (hb_face_t *face,
const hb_tag_t *tags)
{
hb_face_builder_data_t *data = (hb_face_builder_data_t *) face->user_data;
// Sort all unspecified tables after any specified tables.
for (auto& info : data->tables.values_ref())
info.order = -1;
unsigned order = 0;
for (const hb_tag_t* tag = tags;
*tag;
tag++)
{
face_table_info_t* info;
if (!data->tables.has (*tag, &info)) continue;
info->order = order++;
}
}

View file

@ -171,6 +171,10 @@ hb_face_builder_add_table (hb_face_t *face,
hb_tag_t tag,
hb_blob_t *blob);
HB_EXTERN void
hb_face_builder_sort_tables (hb_face_t *face,
const hb_tag_t *tags);
HB_END_DECLS

View file

@ -133,6 +133,18 @@ struct
template <typename T> constexpr auto
operator () (T *v) const HB_AUTO_RETURN (*v)
template <typename T> constexpr auto
operator () (const hb::shared_ptr<T>& v) const HB_AUTO_RETURN (*v)
template <typename T> constexpr auto
operator () (hb::shared_ptr<T>& v) const HB_AUTO_RETURN (*v)
template <typename T> constexpr auto
operator () (const hb::unique_ptr<T>& v) const HB_AUTO_RETURN (*v)
template <typename T> constexpr auto
operator () (hb::unique_ptr<T>& v) const HB_AUTO_RETURN (*v)
}
HB_FUNCOBJ (hb_deref);

View file

@ -141,27 +141,24 @@ typedef HBINT32 FWORD32;
/* 16-bit unsigned integer (HBUINT16) that describes a quantity in FUnits. */
typedef HBUINT16 UFWORD;
/* 16-bit signed fixed number with the low 14 bits of fraction (2.14). */
struct F2DOT14 : HBINT16
template <typename Type, unsigned fraction_bits>
struct HBFixed : Type
{
F2DOT14& operator = (uint16_t i ) { HBINT16::operator= (i); return *this; }
// 16384 means 1<<14
float to_float () const { return ((int32_t) v) / 16384.f; }
void set_float (float f) { v = roundf (f * 16384.f); }
static constexpr float shift = (float) (1 << fraction_bits);
static_assert (Type::static_size * 8 > fraction_bits, "");
HBFixed& operator = (typename Type::type i ) { Type::operator= (i); return *this; }
float to_float () const { return ((int32_t) Type::v) / shift; }
void set_float (float f) { Type::v = roundf (f * shift); }
public:
DEFINE_SIZE_STATIC (2);
DEFINE_SIZE_STATIC (Type::static_size);
};
/* 16-bit signed fixed number with the low 14 bits of fraction (2.14). */
using F2DOT14 = HBFixed<HBINT16, 14>;
/* 32-bit signed fixed-point number (16.16). */
struct HBFixed : HBINT32
{
HBFixed& operator = (uint32_t i) { HBINT32::operator= (i); return *this; }
// 65536 means 1<<16
float to_float () const { return ((int32_t) v) / 65536.f; }
void set_float (float f) { v = roundf (f * 65536.f); }
public:
DEFINE_SIZE_STATIC (4);
};
using F16DOT16 = HBFixed<HBINT32, 16>;
/* Date represented in number of seconds since 12:00 midnight, January 1,
* 1904. The value is represented as a signed 64-bit integer. */

View file

@ -358,14 +358,14 @@ struct Affine2x3
return_trace (c->check_struct (this));
}
HBFixed xx;
HBFixed yx;
HBFixed xy;
HBFixed yy;
HBFixed dx;
HBFixed dy;
F16DOT16 xx;
F16DOT16 yx;
F16DOT16 xy;
F16DOT16 yy;
F16DOT16 dx;
F16DOT16 dy;
public:
DEFINE_SIZE_STATIC (6 * HBFixed::static_size);
DEFINE_SIZE_STATIC (6 * F16DOT16::static_size);
};
struct PaintColrLayers

View file

@ -295,8 +295,8 @@ hb_ot_color_has_png (hb_face_t *face)
* @glyph: a glyph index
*
* Fetches the PNG image for a glyph. This function takes a font object, not a face object,
* as input. To get an optimally sized PNG blob, the UPEM value must be set on the @font
* object. If UPEM is unset, the blob returned will be the largest PNG available.
* as input. To get an optimally sized PNG blob, the PPEM values must be set on the @font
* object. If PPEM is unset, the blob returned will be the largest PNG available.
*
* If the glyph has no PNG image, the singleton empty blob is returned.
*

View file

@ -40,7 +40,6 @@
#include "hb-ot-cff1-table.hh"
#include "hb-ot-cff2-table.hh"
#include "hb-ot-hmtx-table.hh"
#include "hb-ot-os2-table.hh"
#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"
@ -349,15 +348,13 @@ hb_ot_get_glyph_extents (hb_font_t *font,
#if !defined(HB_NO_OT_FONT_BITMAP) && !defined(HB_NO_COLOR)
if (ot_face->sbix->get_extents (font, glyph, extents)) return true;
if (ot_face->CBDT->get_extents (font, glyph, extents)) return true;
#endif
if (ot_face->glyf->get_extents (font, glyph, extents)) return true;
#ifndef HB_NO_OT_FONT_CFF
if (ot_face->cff1->get_extents (font, glyph, extents)) return true;
if (ot_face->cff2->get_extents (font, glyph, extents)) return true;
#endif
#if !defined(HB_NO_OT_FONT_BITMAP) && !defined(HB_NO_COLOR)
if (ot_face->CBDT->get_extents (font, glyph, extents)) return true;
#endif
// TODO Hook up side-bearings variations.
return false;

View file

@ -94,6 +94,19 @@ static bool ClassDef_remap_and_serialize (
hb_sorted_vector_t<hb_pair_t<hb_codepoint_t, hb_codepoint_t>> &glyph_and_klass, /* IN/OUT */
hb_map_t *klass_map /*IN/OUT*/);
struct hb_collect_feature_substitutes_with_var_context_t
{
const hb_map_t *axes_index_tag_map;
const hb_hashmap_t<hb_tag_t, int> *axes_location;
hb_hashmap_t<unsigned, hb::shared_ptr<hb_set_t>> *record_cond_idx_map;
hb_hashmap_t<unsigned, const Feature*> *feature_substitutes_map;
// not stored in subset_plan
hb_set_t *feature_indices;
bool apply;
unsigned cur_record_idx;
hb_hashmap_t<hb::shared_ptr<hb_map_t>, unsigned> *conditionset_map;
};
struct hb_prune_langsys_context_t
{
@ -160,24 +173,40 @@ struct hb_subset_layout_context_t :
const hb_map_t *lookup_index_map;
const hb_hashmap_t<unsigned, hb::unique_ptr<hb_set_t>> *script_langsys_map;
const hb_map_t *feature_index_map;
const hb_hashmap_t<unsigned, const Feature*> *feature_substitutes_map;
hb_hashmap_t<unsigned, hb::shared_ptr<hb_set_t>> *feature_record_cond_idx_map;
unsigned cur_script_index;
unsigned cur_feature_var_record_idx;
hb_subset_layout_context_t (hb_subset_context_t *c_,
hb_tag_t tag_,
hb_map_t *lookup_map_,
hb_hashmap_t<unsigned, hb::unique_ptr<hb_set_t>> *script_langsys_map_,
hb_map_t *feature_index_map_) :
hb_tag_t tag_) :
subset_context (c_),
table_tag (tag_),
lookup_index_map (lookup_map_),
script_langsys_map (script_langsys_map_),
feature_index_map (feature_index_map_),
cur_script_index (0xFFFFu),
cur_feature_var_record_idx (0u),
script_count (0),
langsys_count (0),
feature_index_count (0),
lookup_index_count (0)
{}
{
if (tag_ == HB_OT_TAG_GSUB)
{
lookup_index_map = c_->plan->gsub_lookups;
script_langsys_map = c_->plan->gsub_langsys;
feature_index_map = c_->plan->gsub_features;
feature_substitutes_map = c_->plan->gsub_feature_substitutes_map;
feature_record_cond_idx_map = c_->plan->user_axes_location->is_empty () ? nullptr : c_->plan->gsub_feature_record_cond_idx_map;
}
else
{
lookup_index_map = c_->plan->gpos_lookups;
script_langsys_map = c_->plan->gpos_langsys;
feature_index_map = c_->plan->gpos_features;
feature_substitutes_map = c_->plan->gpos_feature_substitutes_map;
feature_record_cond_idx_map = c_->plan->user_axes_location->is_empty () ? nullptr : c_->plan->gpos_feature_record_cond_idx_map;
}
}
private:
unsigned script_count;
@ -324,6 +353,31 @@ struct subset_record_array_t
const void *base;
};
template<typename OutputArray, typename Arg>
struct subset_record_array_arg_t
{
subset_record_array_arg_t (hb_subset_layout_context_t *c_, OutputArray* out_,
const void *base_,
Arg &&arg_) : subset_layout_context (c_),
out (out_), base (base_), arg (arg_) {}
template <typename T>
void
operator () (T&& record)
{
auto snap = subset_layout_context->subset_context->serializer->snapshot ();
bool ret = record.subset (subset_layout_context, base, arg);
if (!ret) subset_layout_context->subset_context->serializer->revert (snap);
else out->len++;
}
private:
hb_subset_layout_context_t *subset_layout_context;
OutputArray *out;
const void *base;
Arg &&arg;
};
/*
* Helper to subset a RecordList/record array. Subsets each Record in the array and
* discards the record if the subset operation returns false.
@ -335,6 +389,13 @@ struct
operator () (hb_subset_layout_context_t *c, OutputArray* out,
const void *base) const
{ return subset_record_array_t<OutputArray> (c, out, base); }
/* Variant with one extra argument passed to subset */
template<typename OutputArray, typename Arg>
subset_record_array_arg_t<OutputArray, Arg>
operator () (hb_subset_layout_context_t *c, OutputArray* out,
const void *base, Arg &&arg) const
{ return subset_record_array_arg_t<OutputArray, Arg> (c, out, base, arg); }
}
HB_FUNCOBJ (subset_record_array);
@ -431,94 +492,6 @@ struct IndexArray : Array16Of<Index>
};
struct Record_sanitize_closure_t {
hb_tag_t tag;
const void *list_base;
};
template <typename Type>
struct Record
{
int cmp (hb_tag_t a) const { return tag.cmp (a); }
bool subset (hb_subset_layout_context_t *c, const void *base) const
{
TRACE_SUBSET (this);
auto *out = c->subset_context->serializer->embed (this);
if (unlikely (!out)) return_trace (false);
bool ret = out->offset.serialize_subset (c->subset_context, offset, base, c, &tag);
return_trace (ret);
}
bool sanitize (hb_sanitize_context_t *c, const void *base) const
{
TRACE_SANITIZE (this);
const Record_sanitize_closure_t closure = {tag, base};
return_trace (c->check_struct (this) && offset.sanitize (c, base, &closure));
}
Tag tag; /* 4-byte Tag identifier */
Offset16To<Type>
offset; /* Offset from beginning of object holding
* the Record */
public:
DEFINE_SIZE_STATIC (6);
};
template <typename Type>
struct RecordArrayOf : SortedArray16Of<Record<Type>>
{
const Offset16To<Type>& get_offset (unsigned int i) const
{ return (*this)[i].offset; }
Offset16To<Type>& get_offset (unsigned int i)
{ return (*this)[i].offset; }
const Tag& get_tag (unsigned int i) const
{ return (*this)[i].tag; }
unsigned int get_tags (unsigned int start_offset,
unsigned int *record_count /* IN/OUT */,
hb_tag_t *record_tags /* OUT */) const
{
if (record_count)
{
+ this->sub_array (start_offset, record_count)
| hb_map (&Record<Type>::tag)
| hb_sink (hb_array (record_tags, *record_count))
;
}
return this->len;
}
bool find_index (hb_tag_t tag, unsigned int *index) const
{
return this->bfind (tag, index, HB_NOT_FOUND_STORE, Index::NOT_FOUND_INDEX);
}
};
template <typename Type>
struct RecordListOf : RecordArrayOf<Type>
{
const Type& operator [] (unsigned int i) const
{ return this+this->get_offset (i); }
bool subset (hb_subset_context_t *c,
hb_subset_layout_context_t *l) const
{
TRACE_SUBSET (this);
auto *out = c->serializer->start_embed (*this);
if (unlikely (!c->serializer->extend_min (out))) return_trace (false);
+ this->iter ()
| hb_apply (subset_record_array (l, out, this))
;
return_trace (true);
}
bool sanitize (hb_sanitize_context_t *c) const
{
TRACE_SANITIZE (this);
return_trace (RecordArrayOf<Type>::sanitize (c, this));
}
};
/* https://docs.microsoft.com/en-us/typography/opentype/spec/features_pt#size */
struct FeatureParamsSize
{
@ -801,6 +774,10 @@ struct FeatureParams
DEFINE_SIZE_MIN (0);
};
struct Record_sanitize_closure_t {
hb_tag_t tag;
const void *list_base;
};
struct Feature
{
@ -897,6 +874,103 @@ struct Feature
DEFINE_SIZE_ARRAY_SIZED (4, lookupIndex);
};
template <typename Type>
struct Record
{
int cmp (hb_tag_t a) const { return tag.cmp (a); }
bool subset (hb_subset_layout_context_t *c, const void *base, const void *f_sub = nullptr) const
{
TRACE_SUBSET (this);
auto *out = c->subset_context->serializer->embed (this);
if (unlikely (!out)) return_trace (false);
if (!f_sub)
return_trace (out->offset.serialize_subset (c->subset_context, offset, base, c, &tag));
const Feature& f = *reinterpret_cast<const Feature *> (f_sub);
auto *s = c->subset_context->serializer;
s->push ();
out->offset = 0;
bool ret = f.subset (c->subset_context, c, &tag);
if (ret)
s->add_link (out->offset, s->pop_pack ());
else
s->pop_discard ();
return_trace (ret);
}
bool sanitize (hb_sanitize_context_t *c, const void *base) const
{
TRACE_SANITIZE (this);
const Record_sanitize_closure_t closure = {tag, base};
return_trace (c->check_struct (this) && offset.sanitize (c, base, &closure));
}
Tag tag; /* 4-byte Tag identifier */
Offset16To<Type>
offset; /* Offset from beginning of object holding
* the Record */
public:
DEFINE_SIZE_STATIC (6);
};
template <typename Type>
struct RecordArrayOf : SortedArray16Of<Record<Type>>
{
const Offset16To<Type>& get_offset (unsigned int i) const
{ return (*this)[i].offset; }
Offset16To<Type>& get_offset (unsigned int i)
{ return (*this)[i].offset; }
const Tag& get_tag (unsigned int i) const
{ return (*this)[i].tag; }
unsigned int get_tags (unsigned int start_offset,
unsigned int *record_count /* IN/OUT */,
hb_tag_t *record_tags /* OUT */) const
{
if (record_count)
{
+ this->sub_array (start_offset, record_count)
| hb_map (&Record<Type>::tag)
| hb_sink (hb_array (record_tags, *record_count))
;
}
return this->len;
}
bool find_index (hb_tag_t tag, unsigned int *index) const
{
return this->bfind (tag, index, HB_NOT_FOUND_STORE, Index::NOT_FOUND_INDEX);
}
};
template <typename Type>
struct RecordListOf : RecordArrayOf<Type>
{
const Type& operator [] (unsigned int i) const
{ return this+this->get_offset (i); }
bool subset (hb_subset_context_t *c,
hb_subset_layout_context_t *l) const
{
TRACE_SUBSET (this);
auto *out = c->serializer->start_embed (*this);
if (unlikely (!c->serializer->extend_min (out))) return_trace (false);
+ this->iter ()
| hb_apply (subset_record_array (l, out, this))
;
return_trace (true);
}
bool sanitize (hb_sanitize_context_t *c) const
{
TRACE_SANITIZE (this);
return_trace (RecordArrayOf<Type>::sanitize (c, this));
}
};
struct RecordListOfFeature : RecordListOf<Feature>
{
bool subset (hb_subset_context_t *c,
@ -907,11 +981,20 @@ struct RecordListOfFeature : RecordListOf<Feature>
if (unlikely (!out || !c->serializer->extend_min (out))) return_trace (false);
unsigned count = this->len;
+ hb_zip (*this, hb_range (count))
| hb_filter (l->feature_index_map, hb_second)
| hb_map (hb_first)
| hb_apply (subset_record_array (l, out, this))
| hb_apply ([l, out, this] (const hb_pair_t<const Record<Feature>&, unsigned>& _)
{
const Feature *f_sub = nullptr;
const Feature **f = nullptr;
if (l->feature_substitutes_map->has (_.second, &f))
f_sub = *f;
subset_record_array (l, out, this, f_sub) (_.first);
})
;
return_trace (true);
}
};
@ -2692,6 +2775,13 @@ struct VariationStore
/*
* Feature Variations
*/
enum Cond_with_Var_flag_t
{
KEEP_COND_WITH_VAR = 0,
DROP_COND_WITH_VAR = 1,
DROP_RECORD_WITH_VAR = 2,
MEM_ERR_WITH_VAR = 3,
};
struct ConditionFormat1
{
@ -2702,10 +2792,52 @@ struct ConditionFormat1
TRACE_SUBSET (this);
auto *out = c->serializer->embed (this);
if (unlikely (!out)) return_trace (false);
return_trace (true);
const hb_map_t *index_map = c->plan->axes_index_map;
if (index_map->is_empty ()) return_trace (true);
if (!index_map->has (axisIndex))
return_trace (false);
return_trace (c->serializer->check_assign (out->axisIndex, index_map->get (axisIndex),
HB_SERIALIZE_ERROR_INT_OVERFLOW));
}
private:
Cond_with_Var_flag_t keep_with_variations (hb_collect_feature_substitutes_with_var_context_t *c,
hb_map_t *condition_map /* OUT */) const
{
//invalid axis index, drop the entire record
if (!c->axes_index_tag_map->has (axisIndex))
return DROP_RECORD_WITH_VAR;
hb_tag_t axis_tag = c->axes_index_tag_map->get (axisIndex);
//axis not pinned, keep the condition
if (!c->axes_location->has (axis_tag))
{
// add axisIndex->value into the hashmap so we can check if the record is
// unique with variations
int16_t min_val = filterRangeMinValue;
int16_t max_val = filterRangeMaxValue;
hb_codepoint_t val = (max_val << 16) + min_val;
condition_map->set (axisIndex, val);
return KEEP_COND_WITH_VAR;
}
//axis pinned, check if condition is met
//TODO: add check for axis Ranges
int v = c->axes_location->get (axis_tag);
//condition not met, drop the entire record
if (v < filterRangeMinValue || v > filterRangeMaxValue)
return DROP_RECORD_WITH_VAR;
//axis pinned and condition met, drop the condition
return DROP_COND_WITH_VAR;
}
bool evaluate (const int *coords, unsigned int coord_len) const
{
int coord = axisIndex < coord_len ? coords[axisIndex] : 0;
@ -2737,6 +2869,15 @@ struct Condition
}
}
Cond_with_Var_flag_t keep_with_variations (hb_collect_feature_substitutes_with_var_context_t *c,
hb_map_t *condition_map /* OUT */) const
{
switch (u.format) {
case 1: return u.format1.keep_with_variations (c, condition_map);
default:return KEEP_COND_WITH_VAR;
}
}
template <typename context_t, typename ...Ts>
typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const
{
@ -2778,15 +2919,65 @@ struct ConditionSet
return true;
}
bool subset (hb_subset_context_t *c) const
Cond_with_Var_flag_t keep_with_variations (hb_collect_feature_substitutes_with_var_context_t *c) const
{
hb_map_t *condition_map = hb_map_create ();
if (unlikely (!condition_map)) return MEM_ERR_WITH_VAR;
hb::shared_ptr<hb_map_t> p {condition_map};
hb_set_t *cond_set = hb_set_create ();
if (unlikely (!cond_set)) return MEM_ERR_WITH_VAR;
hb::shared_ptr<hb_set_t> s {cond_set};
unsigned num_kept_cond = 0, cond_idx = 0;
for (const auto& offset : conditions)
{
Cond_with_Var_flag_t ret = (this+offset).keep_with_variations (c, condition_map);
// one condition is not met, drop the entire record
if (ret == DROP_RECORD_WITH_VAR)
return DROP_RECORD_WITH_VAR;
// axis not pinned, keep this condition
if (ret == KEEP_COND_WITH_VAR)
{
cond_set->add (cond_idx);
num_kept_cond++;
}
cond_idx++;
}
// all conditions met
if (num_kept_cond == 0) return DROP_COND_WITH_VAR;
//check if condition_set is unique with variations
if (c->conditionset_map->has (p))
//duplicate found, drop the entire record
return DROP_RECORD_WITH_VAR;
c->conditionset_map->set (p, 1);
c->record_cond_idx_map->set (c->cur_record_idx, s);
return KEEP_COND_WITH_VAR;
}
bool subset (hb_subset_context_t *c,
hb_subset_layout_context_t *l) const
{
TRACE_SUBSET (this);
auto *out = c->serializer->start_embed (this);
if (unlikely (!out || !c->serializer->extend_min (out))) return_trace (false);
+ conditions.iter ()
| hb_apply (subset_offset_array (c, out->conditions, this))
;
hb_set_t *retained_cond_set = nullptr;
if (l->feature_record_cond_idx_map != nullptr)
retained_cond_set = l->feature_record_cond_idx_map->get (l->cur_feature_var_record_idx);
unsigned int count = conditions.len;
for (unsigned int i = 0; i < count; i++)
{
if (retained_cond_set != nullptr && !retained_cond_set->has (i))
continue;
subset_offset_array (c, out->conditions, this) (conditions[i]);
}
return_trace (bool (out->conditions));
}
@ -2820,10 +3011,19 @@ struct FeatureTableSubstitutionRecord
feature_indexes->add (featureIndex);
}
void collect_feature_substitutes_with_variations (hb_hashmap_t<unsigned, const Feature*> *feature_substitutes_map,
const hb_set_t *feature_indices,
const void *base) const
{
if (feature_indices->has (featureIndex))
feature_substitutes_map->set (featureIndex, &(base+feature));
}
bool subset (hb_subset_layout_context_t *c, const void *base) const
{
TRACE_SUBSET (this);
if (!c->feature_index_map->has (featureIndex)) {
if (!c->feature_index_map->has (featureIndex) ||
c->feature_substitutes_map->has (featureIndex)) {
// Feature that is being substituted is not being retained, so we don't
// need this.
return_trace (false);
@ -2865,10 +3065,16 @@ struct FeatureTableSubstitution
}
void collect_lookups (const hb_set_t *feature_indexes,
const hb_hashmap_t<unsigned, const Feature*> *feature_substitutes_map,
hb_set_t *lookup_indexes /* OUT */) const
{
+ hb_iter (substitutions)
| hb_filter (feature_indexes, &FeatureTableSubstitutionRecord::featureIndex)
| hb_filter ([feature_substitutes_map] (const FeatureTableSubstitutionRecord& record)
{
if (feature_substitutes_map == nullptr) return true;
return !feature_substitutes_map->has (record.featureIndex);
})
| hb_apply ([this, lookup_indexes] (const FeatureTableSubstitutionRecord& r)
{ r.collect_lookups (this, lookup_indexes); })
;
@ -2890,6 +3096,12 @@ struct FeatureTableSubstitution
return false;
}
void collect_feature_substitutes_with_variations (hb_collect_feature_substitutes_with_var_context_t *c) const
{
for (const FeatureTableSubstitutionRecord& record : substitutions)
record.collect_feature_substitutes_with_variations (c->feature_substitutes_map, c->feature_indices, this);
}
bool subset (hb_subset_context_t *c,
hb_subset_layout_context_t *l) const
{
@ -2929,9 +3141,10 @@ struct FeatureVariationRecord
void collect_lookups (const void *base,
const hb_set_t *feature_indexes,
const hb_hashmap_t<unsigned, const Feature*> *feature_substitutes_map,
hb_set_t *lookup_indexes /* OUT */) const
{
return (base+substitutions).collect_lookups (feature_indexes, lookup_indexes);
return (base+substitutions).collect_lookups (feature_indexes, feature_substitutes_map, lookup_indexes);
}
void closure_features (const void *base,
@ -2946,13 +3159,25 @@ struct FeatureVariationRecord
return (base+substitutions).intersects_features (feature_index_map);
}
void collect_feature_substitutes_with_variations (hb_collect_feature_substitutes_with_var_context_t *c,
const void *base) const
{
// ret == 1, all conditions met
if ((base+conditions).keep_with_variations (c) == DROP_COND_WITH_VAR &&
c->apply)
{
(base+substitutions).collect_feature_substitutes_with_variations (c);
c->apply = false; // set variations only once
}
}
bool subset (hb_subset_layout_context_t *c, const void *base) const
{
TRACE_SUBSET (this);
auto *out = c->subset_context->serializer->embed (this);
if (unlikely (!out)) return_trace (false);
out->conditions.serialize_subset (c->subset_context, conditions, base);
out->conditions.serialize_subset (c->subset_context, conditions, base, c);
out->substitutions.serialize_subset (c->subset_context, substitutions, base, c);
return_trace (true);
@ -3002,6 +3227,16 @@ struct FeatureVariations
return (this+record.substitutions).find_substitute (feature_index);
}
void collect_feature_substitutes_with_variations (hb_collect_feature_substitutes_with_var_context_t *c) const
{
unsigned int count = varRecords.len;
for (unsigned int i = 0; i < count; i++)
{
c->cur_record_idx = i;
varRecords[i].collect_feature_substitutes_with_variations (c, this);
}
}
FeatureVariations* copy (hb_serialize_context_t *c) const
{
TRACE_SERIALIZE (this);
@ -3009,17 +3244,25 @@ struct FeatureVariations
}
void collect_lookups (const hb_set_t *feature_indexes,
const hb_hashmap_t<unsigned, const Feature*> *feature_substitutes_map,
hb_set_t *lookup_indexes /* OUT */) const
{
for (const FeatureVariationRecord& r : varRecords)
r.collect_lookups (this, feature_indexes, lookup_indexes);
r.collect_lookups (this, feature_indexes, feature_substitutes_map, lookup_indexes);
}
void closure_features (const hb_map_t *lookup_indexes,
const hb_hashmap_t<unsigned, hb::shared_ptr<hb_set_t>> *feature_record_cond_idx_map,
hb_set_t *feature_indexes /* OUT */) const
{
for (const FeatureVariationRecord& record : varRecords)
record.closure_features (this, lookup_indexes, feature_indexes);
unsigned int count = varRecords.len;
for (unsigned int i = 0; i < count; i++)
{
if (feature_record_cond_idx_map != nullptr &&
!feature_record_cond_idx_map->has (i))
continue;
varRecords[i].closure_features (this, lookup_indexes, feature_indexes);
}
}
bool subset (hb_subset_context_t *c,
@ -3041,7 +3284,13 @@ struct FeatureVariations
}
unsigned count = (unsigned) (keep_up_to + 1);
for (unsigned i = 0; i < count; i++) {
for (unsigned i = 0; i < count; i++)
{
if (l->feature_record_cond_idx_map != nullptr &&
!l->feature_record_cond_idx_map->has (i))
continue;
l->cur_feature_var_record_idx = i;
subset_record_array (l, &(out->varRecords), this) (varRecords[i]);
}
return_trace (bool (out->varRecords));

View file

@ -4236,13 +4236,19 @@ struct GSUBGPOS
}
void feature_variation_collect_lookups (const hb_set_t *feature_indexes,
const hb_hashmap_t<unsigned, const Feature*> *feature_substitutes_map,
hb_set_t *lookup_indexes /* OUT */) const
{
#ifndef HB_NO_VAR
get_feature_variations ().collect_lookups (feature_indexes, lookup_indexes);
get_feature_variations ().collect_lookups (feature_indexes, feature_substitutes_map, lookup_indexes);
#endif
}
#ifndef HB_NO_VAR
void collect_feature_substitutes_with_variations (hb_collect_feature_substitutes_with_var_context_t *c) const
{ get_feature_variations ().collect_feature_substitutes_with_variations (c); }
#endif
template <typename TLookup>
void closure_lookups (hb_face_t *face,
const hb_set_t *glyphs,
@ -4278,6 +4284,8 @@ struct GSUBGPOS
}
void prune_features (const hb_map_t *lookup_indices, /* IN */
const hb_hashmap_t<unsigned, hb::shared_ptr<hb_set_t>> *feature_record_cond_idx_map, /* IN */
const hb_hashmap_t<unsigned, const Feature*> *feature_substitutes_map, /* IN */
hb_set_t *feature_indices /* IN/OUT */) const
{
#ifndef HB_NO_VAR
@ -4285,7 +4293,7 @@ struct GSUBGPOS
// if the FeatureVariation's table and the alternate version(s) intersect the
// set of lookup indices.
hb_set_t alternate_feature_indices;
get_feature_variations ().closure_features (lookup_indices, &alternate_feature_indices);
get_feature_variations ().closure_features (lookup_indices, feature_record_cond_idx_map, &alternate_feature_indices);
if (unlikely (alternate_feature_indices.in_error()))
{
feature_indices->err ();
@ -4295,7 +4303,6 @@ struct GSUBGPOS
for (unsigned i : feature_indices->iter())
{
const Feature& f = get_feature (i);
hb_tag_t tag = get_feature_tag (i);
if (tag == HB_TAG ('p', 'r', 'e', 'f'))
// Note: Never ever drop feature 'pref', even if it's empty.
@ -4305,11 +4312,16 @@ struct GSUBGPOS
continue;
if (!f.featureParams.is_null () &&
const Feature *f = &(get_feature (i));
const Feature** p = nullptr;
if (feature_substitutes_map->has (i, &p))
f = *p;
if (!f->featureParams.is_null () &&
tag == HB_TAG ('s', 'i', 'z', 'e'))
continue;
if (!f.intersects_lookup_indexes (lookup_indices)
if (!f->intersects_lookup_indexes (lookup_indices)
#ifndef HB_NO_VAR
&& !alternate_feature_indices.has (i)
#endif

View file

@ -1271,7 +1271,7 @@ hb_ot_layout_collect_lookups (hb_face_t *face,
hb_set_next (&feature_indexes, &feature_index);)
g.get_feature (feature_index).add_lookup_indexes_to (lookup_indexes);
g.feature_variation_collect_lookups (&feature_indexes, lookup_indexes);
g.feature_variation_collect_lookups (&feature_indexes, nullptr, lookup_indexes);
}
@ -1709,6 +1709,8 @@ hb_ot_layout_get_size_params (hb_face_t *face,
return false;
}
/**
* hb_ot_layout_feature_get_name_ids:
* @face: #hb_face_t to work upon
@ -2341,6 +2343,7 @@ struct hb_get_glyph_alternates_dispatch_t :
( _dispatch (obj, hb_prioritize, std::forward<Ts> (ds)...) )
};
#ifndef HB_NO_LAYOUT_RARELY_USED
/**
* hb_ot_layout_lookup_get_glyph_alternates:
* @face: a face.
@ -2373,4 +2376,72 @@ hb_ot_layout_lookup_get_glyph_alternates (hb_face_t *face,
return ret;
}
struct hb_position_single_dispatch_t :
hb_dispatch_context_t<hb_position_single_dispatch_t, bool>
{
static return_t default_return_value () { return false; }
bool stop_sublookup_iteration (return_t r) const { return r; }
private:
template <typename T, typename ...Ts> auto
_dispatch (const T &obj, hb_priority<1>, Ts&&... ds) HB_AUTO_RETURN
( obj.position_single (std::forward<Ts> (ds)...) )
template <typename T, typename ...Ts> auto
_dispatch (const T &obj, hb_priority<0>, Ts&&... ds) HB_AUTO_RETURN
( default_return_value () )
public:
template <typename T, typename ...Ts> auto
dispatch (const T &obj, Ts&&... ds) HB_AUTO_RETURN
( _dispatch (obj, hb_prioritize, std::forward<Ts> (ds)...) )
};
/**
* hb_ot_layout_lookup_get_optical_bound:
* @font: a font.
* @lookup_index: index of the feature lookup to query.
* @direction: edge of the glyph to query.
* @glyph: a glyph id.
*
* Fetches the optical bound of a glyph positioned at the margin of text.
* The direction identifies which edge of the glyph to query.
*
* Return value: Adjustment value. Negative values mean the glyph will stick out of the margin.
*
* Since: 5.3.0
**/
hb_position_t
hb_ot_layout_lookup_get_optical_bound (hb_font_t *font,
unsigned lookup_index,
hb_direction_t direction,
hb_codepoint_t glyph)
{
const OT::PosLookup &lookup = font->face->table.GPOS->table->get_lookup (lookup_index);
hb_glyph_position_t pos = {0};
hb_position_single_dispatch_t c;
lookup.dispatch (&c, font, direction, glyph, pos);
hb_position_t ret = 0;
switch (direction)
{
case HB_DIRECTION_LTR:
ret = pos.x_offset;
break;
case HB_DIRECTION_RTL:
ret = pos.x_advance - pos.x_offset;
break;
case HB_DIRECTION_TTB:
ret = pos.y_offset;
break;
case HB_DIRECTION_BTT:
ret = pos.y_advance - pos.y_offset;
break;
case HB_DIRECTION_INVALID:
default:
break;
}
return ret;
}
#endif
#endif

View file

@ -403,6 +403,16 @@ hb_ot_layout_get_size_params (hb_face_t *face,
unsigned int *range_start, /* OUT. May be NULL */
unsigned int *range_end /* OUT. May be NULL */);
HB_EXTERN hb_position_t
hb_ot_layout_lookup_get_optical_bound (hb_font_t *font,
unsigned lookup_index,
hb_direction_t direction,
hb_codepoint_t glyph);
/*
* GSUB/GPOS
*/
HB_EXTERN hb_bool_t
hb_ot_layout_feature_get_name_ids (hb_face_t *face,
@ -423,6 +433,7 @@ hb_ot_layout_feature_get_characters (hb_face_t *face,
unsigned int *char_count /* IN/OUT. May be NULL */,
hb_codepoint_t *characters /* OUT. May be NULL */);
/*
* BASE
*/

View file

@ -282,7 +282,7 @@ struct post
* 0x00020000 for version 2.0
* 0x00025000 for version 2.5 (deprecated)
* 0x00030000 for version 3.0 */
HBFixed italicAngle; /* Italic angle in counter-clockwise degrees
F16DOT16 italicAngle; /* Italic angle in counter-clockwise degrees
* from the vertical. Zero for upright text,
* negative for text that leans to the right
* (forward). */

View file

@ -527,18 +527,20 @@ hb_set_unicode_props (hb_buffer_t *buffer)
}
#endif
/* Or part of the Other_Grapheme_Extend that is not marks.
* As of Unicode 11 that is just:
* As of Unicode 15 that is just:
*
* 200C ; Other_Grapheme_Extend # Cf ZERO WIDTH NON-JOINER
* FF9E..FF9F ; Other_Grapheme_Extend # Lm [2] HALFWIDTH KATAKANA VOICED SOUND MARK..HALFWIDTH KATAKANA SEMI-VOICED SOUND MARK
* E0020..E007F ; Other_Grapheme_Extend # Cf [96] TAG SPACE..CANCEL TAG
*
* ZWNJ is special, we don't want to merge it as there's no need, and keeping
* it separate results in more granular clusters. Ignore Katakana for now.
* it separate results in more granular clusters.
* Tags are used for Emoji sub-region flag sequences:
* https://github.com/harfbuzz/harfbuzz/issues/1556
* Katakana ones were requested:
* https://github.com/harfbuzz/harfbuzz/issues/3844
*/
else if (unlikely (hb_in_range<hb_codepoint_t> (info[i].codepoint, 0xE0020u, 0xE007Fu)))
else if (unlikely (hb_in_ranges<hb_codepoint_t> (info[i].codepoint, 0xFF9Eu, 0xFF9Fu, 0xE0020u, 0xE007Fu)))
_hb_glyph_info_set_continuation (&info[i]);
}
}

View file

@ -25,6 +25,7 @@
* # Updated for Unicode 12.1 by Andrew Glass 2019-05-24
* # Updated for Unicode 13.0 by Andrew Glass 2020-07-28
* # Updated for Unicode 14.0 by Andrew Glass 2021-09-25
* # Updated for Unicode 15.0 by Andrew Glass 2022-09-16
* # Override values For Indic_Positional_Category
* # Not derivable
* # Initial version based on Unicode 7.0 by Andrew Glass 2014-03-17
@ -34,6 +35,7 @@
* # Updated for Unicode 12.1 by Andrew Glass 2019-05-30
* # Updated for Unicode 13.0 by Andrew Glass 2020-07-28
* # Updated for Unicode 14.0 by Andrew Glass 2021-09-28
* # Updated for Unicode 15.0 by Andrew Glass 2022-09-16
* UnicodeData.txt does not have a header.
*/
@ -90,7 +92,7 @@
#pragma GCC diagnostic pop
static const uint8_t
hb_use_u8[3115] =
hb_use_u8[3141] =
{
16, 50, 51, 51, 51, 52, 51, 83, 118, 131, 51, 57, 58, 179, 195, 61,
51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51,
@ -125,11 +127,11 @@ hb_use_u8[3115] =
2, 2, 2, 2, 2, 2, 2, 2, 2, 88, 89, 2, 2, 2, 2, 2,
2, 2, 2, 90, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 91, 2, 2, 92, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 93, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 94, 94, 95, 96, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94,
94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94,
94, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
2, 2, 2, 91, 2, 2, 92, 2, 2, 2, 93, 2, 2, 2, 2, 2,
2, 2, 2, 94, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 95, 95, 96, 97, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95,
95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95,
95, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
0, 2, 2, 2, 2, 2, 0, 0, 0, 3, 0, 0, 0, 0, 0, 4,
0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 6, 7, 0, 0, 0, 0, 0, 0,
@ -226,70 +228,72 @@ hb_use_u8[3115] =
0, 9, 47, 2, 2, 2, 2, 2, 2, 2, 2, 2, 125, 18, 20, 151,
20, 19, 152, 153, 2, 2, 2, 2, 2, 0, 0, 63, 154, 0, 0, 0,
0, 2, 11, 0, 0, 0, 0, 0, 0, 2, 63, 23, 18, 18, 18, 20,
20, 106, 155, 0, 0, 156, 157, 29, 158, 28, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 21, 17, 20, 20, 159, 42, 0, 0, 0,
20, 106, 155, 0, 0, 54, 156, 29, 157, 28, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 21, 17, 20, 20, 158, 42, 0, 0, 0,
47, 125, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 7, 7, 2, 2,
28, 2, 2, 2, 2, 2, 2, 2, 28, 2, 2, 2, 2, 2, 2, 2,
8, 16, 17, 19, 20, 160, 29, 0, 0, 9, 9, 28, 2, 2, 2, 7,
8, 16, 17, 19, 20, 159, 29, 0, 0, 9, 9, 28, 2, 2, 2, 7,
28, 7, 2, 28, 2, 2, 56, 15, 21, 14, 21, 45, 30, 31, 30, 32,
0, 0, 0, 0, 33, 0, 0, 0, 2, 2, 21, 0, 9, 9, 9, 44,
0, 9, 9, 44, 0, 0, 0, 0, 0, 2, 2, 63, 23, 18, 18, 18,
20, 21, 123, 13, 15, 0, 0, 0, 0, 2, 2, 2, 2, 2, 0, 0,
161, 162, 0, 0, 0, 0, 0, 0, 0, 16, 17, 18, 18, 64, 97, 23,
158, 9, 163, 7, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2,
63, 23, 18, 18, 0, 46, 46, 9, 164, 35, 0, 0, 0, 0, 0, 0,
160, 161, 0, 0, 0, 0, 0, 0, 0, 16, 17, 18, 18, 64, 97, 23,
157, 9, 162, 7, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2,
63, 23, 18, 18, 0, 46, 46, 9, 163, 35, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 2, 2, 18, 0, 21, 17, 18, 18, 19, 14, 80,
164, 36, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 8, 165,
23, 18, 20, 20, 163, 7, 0, 0, 0, 2, 2, 2, 2, 2, 7, 41,
163, 36, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 8, 164,
23, 18, 20, 20, 162, 7, 0, 0, 0, 2, 2, 2, 2, 2, 7, 41,
133, 21, 20, 18, 74, 19, 20, 0, 0, 2, 2, 2, 7, 0, 0, 0,
0, 2, 2, 2, 2, 2, 2, 16, 17, 18, 19, 20, 103, 164, 35, 0,
0, 2, 2, 2, 2, 2, 2, 16, 17, 18, 19, 20, 103, 163, 35, 0,
0, 2, 2, 2, 7, 28, 0, 2, 2, 2, 2, 28, 7, 2, 2, 2,
2, 21, 21, 16, 30, 31, 10, 166, 167, 168, 169, 0, 0, 0, 0, 0,
2, 21, 21, 16, 30, 31, 10, 165, 166, 167, 168, 0, 0, 0, 0, 0,
0, 2, 2, 2, 2, 0, 2, 2, 2, 63, 23, 18, 18, 0, 20, 21,
27, 106, 0, 31, 0, 0, 0, 0, 0, 50, 18, 20, 20, 20, 137, 2,
2, 2, 170, 171, 9, 13, 172, 70, 173, 0, 0, 1, 144, 0, 0, 0,
0, 50, 18, 20, 14, 17, 18, 2, 2, 2, 2, 155, 155, 155, 174, 174,
174, 174, 174, 174, 13, 175, 0, 28, 0, 20, 18, 18, 29, 20, 20, 9,
164, 0, 59, 59, 59, 59, 59, 59, 59, 64, 19, 80, 44, 0, 0, 0,
2, 2, 169, 170, 9, 13, 171, 70, 172, 0, 0, 1, 144, 0, 0, 0,
0, 50, 18, 20, 14, 17, 18, 2, 2, 2, 2, 155, 155, 155, 173, 173,
173, 173, 173, 173, 13, 174, 0, 28, 0, 20, 18, 18, 29, 20, 20, 9,
163, 0, 59, 59, 59, 59, 59, 59, 59, 64, 19, 80, 44, 0, 0, 0,
0, 2, 2, 2, 7, 2, 28, 2, 2, 50, 20, 20, 29, 0, 36, 20,
25, 9, 157, 176, 172, 0, 0, 0, 0, 2, 2, 2, 28, 7, 2, 2,
25, 9, 156, 175, 171, 0, 0, 0, 0, 2, 2, 2, 28, 7, 2, 2,
2, 2, 2, 2, 2, 2, 21, 21, 45, 20, 33, 80, 66, 0, 0, 0,
0, 2, 177, 64, 45, 0, 0, 0, 0, 9, 178, 2, 2, 2, 2, 2,
0, 2, 176, 64, 45, 0, 0, 0, 0, 9, 177, 2, 2, 2, 2, 2,
2, 2, 2, 21, 20, 18, 29, 0, 46, 14, 140, 0, 0, 0, 0, 0,
0, 179, 179, 179, 106, 7, 0, 0, 0, 9, 9, 9, 44, 0, 0, 0,
0, 2, 2, 2, 2, 2, 7, 0, 56, 180, 18, 18, 18, 18, 18, 18,
0, 178, 178, 178, 106, 179, 178, 0, 0, 145, 2, 2, 180, 114, 114, 114,
114, 114, 114, 114, 0, 0, 0, 0, 0, 9, 9, 9, 44, 0, 0, 0,
0, 2, 2, 2, 2, 2, 7, 0, 56, 181, 18, 18, 18, 18, 18, 18,
18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 0, 0, 0,
38, 114, 24, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0,
0, 2, 2, 2, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 0, 56,
35, 0, 4, 118, 118, 118, 119, 0, 0, 9, 9, 9, 47, 2, 2, 2,
0, 2, 2, 2, 2, 2, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2,
44, 2, 2, 2, 2, 2, 2, 9, 9, 2, 2, 42, 42, 42, 90, 0,
0, O, O, O, GB, B, B, GB, O, O, WJ,FMPst,FMPst, O, CGJ, B,
O, B,VMAbv,VMAbv,VMAbv, O,VMAbv, B,CMBlw,CMBlw,CMBlw,VMAbv,VMPst, VAbv, VPst,CMBlw,
B, VPst, VPre, VPst, VBlw, VBlw, VBlw, VBlw, VAbv, VAbv, VAbv, VPst, VPst, VPst, H, VPre,
VPst,VMBlw, O, O, VAbv, GB,VMAbv,VMPst,VMPst, O, B, VBlw, O, O, VPre, VPre,
O, VPre, H, O, VPst,FMAbv, O,CMBlw, O, VAbv, O, VAbv, H, O,VMBlw,VMAbv,
CMAbv, GB, GB, O, MBlw,CMAbv,CMAbv, VPst, VAbv,VMAbv, O, VPst, O, VPre, VPre,VMAbv,
B, O, CS, CS,VMPst, B, VAbv, VAbv, B, R, O, HVM, O, O, FBlw, O,
CMAbv, O,CMBlw, VAbv, VBlw, B, SUB, SUB, SUB, O, SUB, SUB, O, FBlw, O, B,
VPst, VBlw, VPre,VMAbv,VMBlw,VMPst, IS, VAbv, MPst, MPre, MBlw, MBlw, B, MBlw, MBlw, VPst,
VMPst,VMPst, B, MBlw, VPst, VPre, VAbv, VAbv,VMPst,VMPst,VMBlw, B,VMPst, VBlw, VPst, CGJ,
CGJ, VPst,VMAbv,VMAbv,FMAbv, FAbv,CMAbv,FMAbv,VMAbv,FMAbv, VAbv, IS,FMAbv, B,FMAbv, B,
CGJ, WJ, CGJ, GB,CMAbv,CMAbv, B, GB, B, VAbv, SUB, FPst, FPst,VMBlw, FPst, FPst,
FBlw,VMAbv,FMBlw, VAbv, VPre, B, MPre, MBlw, SUB, FAbv, FAbv, MAbv, SUB, Sk, VPst, VAbv,
VMAbv,VMAbv, FAbv,CMAbv, VPst, H, B, O,SMAbv,SMBlw,SMAbv,SMAbv,SMAbv, VPst, IS, VBlw,
FAbv,VMPre,VMPre,FMAbv,CMBlw,VMBlw,VMBlw,VMAbv, CS, O,FMAbv, ZWNJ, CGJ, WJ, WJ, WJ,
O,FMPst, O, O, H, MPst, VPst, H,VMAbv, VAbv,VMBlw, B, VBlw, FPst, VPst, FAbv,
VMPst, B,CMAbv, VAbv, MBlw, MPst, MBlw, H, O, VBlw, MPst, MPre, MAbv, MBlw, O, B,
FAbv, FAbv, FPst, VBlw, B, B, VPre, O,VMPst, IS, O,VMPst, VBlw, VPst,VMBlw,VMBlw,
VMAbv, O, IS,VMBlw, B,VMPst,VMAbv,VMPst, CS, CS, B, N, N, O, HN, VPre,
VBlw, VAbv, IS,CMAbv, O, VPst, B, R, R, O,FMBlw,CMBlw, VAbv, VPre,VMAbv,VMAbv,
H, VAbv,CMBlw,FMAbv, B, CS, CS, H,CMBlw,VMPst, H,VMPst, VAbv,VMAbv, VPst, IS,
R, MPst, R, MPst,CMBlw, B,FMBlw, VBlw,VMAbv, R, MBlw, MBlw, GB, FBlw, FBlw,CMAbv,
IS, VBlw, IS, GB, VAbv, R,VMPst, H, H, O, VBlw,
44, 2, 2, 2, 2, 2, 2, 9, 9, 2, 2, 2, 2, 2, 2, 20,
20, 2, 2, 42, 42, 42, 90, 0, 0, O, O, O, GB, B, B, GB,
O, O, WJ,FMPst,FMPst, O, CGJ, B, O, B,VMAbv,VMAbv,VMAbv, O,VMAbv, B,
CMBlw,CMBlw,CMBlw,VMAbv,VMPst, VAbv, VPst,CMBlw, B, VPst, VPre, VPst, VBlw, VBlw, VBlw, VBlw,
VAbv, VAbv, VAbv, VPst, VPst, VPst, H, VPre, VPst,VMBlw, O, O, VAbv, GB,VMAbv,VMPst,
VMPst, O, B, VBlw, O, O, VPre, VPre, O, VPre, H, O, VPst,FMAbv, O,CMBlw,
O, VAbv, O, VAbv, H, O,VMBlw,VMAbv,CMAbv, GB, GB, O, MBlw,CMAbv,CMAbv, VPst,
VAbv,VMAbv, O, VPst, O, VPre, VPre,VMAbv, B, O, CS, CS,VMPst, B, VAbv, VAbv,
B, R, O, HVM, O, O,FMBlw, O,CMAbv, O,CMBlw, VAbv, VBlw, B, SUB, SUB,
SUB, O, SUB, SUB, O,FMBlw, O, B, VPst, VBlw, VPre,VMAbv,VMBlw,VMPst, IS, VAbv,
MPst, MPre, MBlw, MBlw, B, MBlw, MBlw, VPst,VMPst,VMPst, B, MBlw, VPst, VPre, VAbv, VAbv,
VMPst,VMPst,VMBlw, B,VMPst, VBlw, VPst, CGJ, CGJ, VPst,VMAbv,VMAbv,FMAbv, FAbv,CMAbv,FMAbv,
VMAbv,FMAbv, VAbv, IS,FMAbv, B,FMAbv, B, CGJ, WJ, CGJ, GB,CMAbv,CMAbv, B, GB,
B, VAbv, SUB, FPst, FPst,VMBlw, FPst, FPst, FBlw,VMAbv,FMBlw, VAbv, VPre, B, MPre, MBlw,
SUB, FAbv, FAbv, MAbv, SUB, Sk, VPst, VAbv,VMAbv,VMAbv, FAbv,CMAbv, VPst, H, B, O,
SMAbv,SMBlw,SMAbv,SMAbv,SMAbv, VPst, IS, VBlw, FAbv,VMPre,VMPre,FMAbv,CMBlw,VMBlw,VMBlw,VMAbv,
CS, O,FMAbv, ZWNJ, CGJ, WJ, WJ, WJ, O,FMPst, O, O, H, MPst, VPst, H,
VMAbv, VAbv,VMBlw, B, VBlw, FPst, VPst, FAbv,VMPst, B,CMAbv, VAbv, MBlw, MPst, MBlw, H,
O, VBlw, MPst, MPre, MAbv, MBlw, O, B, FAbv, FAbv, FPst, VBlw, B, B, VPre, O,
VMPst, IS, O,VMPst, VBlw, VPst,VMBlw,VMBlw,VMAbv, O, IS,VMBlw, B,VMPst,VMAbv,VMPst,
CS, CS, B, N, N, O, HN, VPre, VBlw, VAbv, IS,CMAbv, O, VPst, B, R,
R,CMBlw, VAbv, VPre,VMAbv,VMAbv, H, VAbv,CMBlw,FMAbv, B, CS, CS, H,CMBlw,VMPst,
H,VMPst, VAbv,VMAbv, VPst, IS, R, MPst, R, MPst,CMBlw, B,FMBlw, VBlw,VMAbv, R,
MBlw, MBlw, GB, FBlw, FBlw,CMAbv, IS, VBlw, IS, GB, VAbv, R,VMPst, H, H, B,
H, B,VMBlw, O, VBlw,
};
static const uint16_t
hb_use_u16[776] =
hb_use_u16[784] =
{
0, 0, 1, 2, 0, 0, 0, 0, 0, 0, 3, 4, 0, 5, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0,
@ -332,14 +336,14 @@ hb_use_u16[776] =
9,242, 73,243, 0, 0, 0, 0,244, 9, 9,245,246, 2,247, 9,
248,249, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9,250,
251, 48, 9,252,253, 2, 0, 0, 9, 9, 9, 9, 9, 9, 9, 9,
9, 9, 98,254, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0,
9, 9, 9,255, 0, 0, 0, 0, 9, 9, 9, 9,256,257,258,258,
259,260, 0, 0, 0, 0,261, 0, 9, 9, 9, 9, 9,262, 0, 0,
9, 9, 9, 9, 9, 9,105, 70, 94,263, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0,264, 9, 9, 70,265,266, 0, 0, 0,
0, 9,267, 0, 9, 9,268, 2, 9, 9, 9, 9,269, 2, 0, 0,
129,129,129,129,129,129,129,129,160,160,160,160,160,160,160,160,
160,160,160,160,160,160,160,129,
9, 9, 9,254,255,256, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0,
9, 9, 9,257, 0, 0, 0, 0, 9, 9, 9, 9,258,259,260,260,
261,262, 0, 0, 0, 0,263, 0, 9, 9, 9, 9, 9,264, 0, 0,
9, 9, 9, 9, 9, 9,105, 70, 94,265, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0,266, 9, 9, 70,267,268, 0, 0, 0,
0, 9,269, 0, 9, 9,270, 2, 0, 0, 0, 0, 0, 9,271, 2,
9, 9, 9, 9,272, 2, 0, 0,129,129,129,129,129,129,129,129,
160,160,160,160,160,160,160,160,160,160,160,160,160,160,160,129,
};
static inline unsigned
@ -350,7 +354,7 @@ hb_use_b4 (const uint8_t* a, unsigned i)
static inline uint_fast8_t
hb_use_get_category (unsigned u)
{
return u<921600u?hb_use_u8[2753+(((hb_use_u8[593+(((hb_use_u16[((hb_use_u8[113+(((hb_use_b4(hb_use_u8,u>>1>>3>>3>>5))<<5)+((u>>1>>3>>3)&31u))])<<3)+((u>>1>>3)&7u)])<<3)+((u>>1)&7u))])<<1)+((u)&1u))]:O;
return u<921600u?hb_use_u8[2777+(((hb_use_u8[593+(((hb_use_u16[((hb_use_u8[113+(((hb_use_b4(hb_use_u8,u>>1>>3>>3>>5))<<5)+((u>>1>>3>>3)&31u))])<<3)+((u>>1>>3)&7u)])<<3)+((u>>1)&7u))])<<1)+((u)&1u))]:O;
}
#undef B

View file

@ -342,6 +342,40 @@ _hb_preprocess_text_vowel_constraints (const hb_ot_shape_plan_t *plan HB_UNUSED,
}
break;
case HB_SCRIPT_KHOJKI:
for (buffer->idx = 0; buffer->idx + 1 < count && buffer->successful;)
{
bool matched = false;
switch (buffer->cur ().codepoint)
{
case 0x11200u:
switch (buffer->cur (1).codepoint)
{
case 0x1122Cu: case 0x11231u: case 0x11233u:
matched = true;
break;
}
break;
case 0x11206u:
matched = 0x1122Cu == buffer->cur (1).codepoint;
break;
case 0x1122Cu:
switch (buffer->cur (1).codepoint)
{
case 0x11230u: case 0x11231u:
matched = true;
break;
}
break;
case 0x11240u:
matched = 0x1122Eu == buffer->cur (1).codepoint;
break;
}
(void) buffer->next_glyph ();
if (matched) _output_with_dotted_circle (buffer);
}
break;
case HB_SCRIPT_KHUDAWADI:
for (buffer->idx = 0; buffer->idx + 1 < count && buffer->successful;)
{

View file

@ -136,7 +136,7 @@ struct AxisValueFormat1
NameID valueNameID; /* The name ID for entries in the 'name' table
* that provide a display string for this
* attribute value. */
HBFixed value; /* A numeric value for this attribute value. */
F16DOT16 value; /* A numeric value for this attribute value. */
public:
DEFINE_SIZE_STATIC (12);
};
@ -195,10 +195,10 @@ struct AxisValueFormat2
NameID valueNameID; /* The name ID for entries in the 'name' table
* that provide a display string for this
* attribute value. */
HBFixed nominalValue; /* A numeric value for this attribute value. */
HBFixed rangeMinValue; /* The minimum value for a range associated
F16DOT16 nominalValue; /* A numeric value for this attribute value. */
F16DOT16 rangeMinValue; /* The minimum value for a range associated
* with the specified name ID. */
HBFixed rangeMaxValue; /* The maximum value for a range associated
F16DOT16 rangeMaxValue; /* The maximum value for a range associated
* with the specified name ID. */
public:
DEFINE_SIZE_STATIC (20);
@ -258,8 +258,8 @@ struct AxisValueFormat3
NameID valueNameID; /* The name ID for entries in the 'name' table
* that provide a display string for this
* attribute value. */
HBFixed value; /* A numeric value for this attribute value. */
HBFixed linkedValue; /* The numeric value for a style-linked mapping
F16DOT16 value; /* A numeric value for this attribute value. */
F16DOT16 linkedValue; /* The numeric value for a style-linked mapping
* from this value. */
public:
DEFINE_SIZE_STATIC (16);
@ -280,7 +280,7 @@ struct AxisValueRecord
HBUINT16 axisIndex; /* Zero-base index into the axis record array
* identifying the axis to which this value
* applies. Must be less than designAxisCount. */
HBFixed value; /* A numeric value for this attribute value. */
F16DOT16 value; /* A numeric value for this attribute value. */
public:
DEFINE_SIZE_STATIC (6);
};

View file

@ -44,9 +44,47 @@ struct InstanceRecord
{
friend struct fvar;
hb_array_t<const HBFixed> get_coordinates (unsigned int axis_count) const
hb_array_t<const F16DOT16> get_coordinates (unsigned int axis_count) const
{ return coordinatesZ.as_array (axis_count); }
bool subset (hb_subset_context_t *c,
unsigned axis_count,
bool has_postscript_nameid) const
{
TRACE_SUBSET (this);
if (unlikely (!c->serializer->embed (subfamilyNameID))) return_trace (false);
if (unlikely (!c->serializer->embed (flags))) return_trace (false);
const hb_array_t<const F16DOT16> coords = get_coordinates (axis_count);
const hb_hashmap_t<hb_tag_t, float> *axes_location = c->plan->user_axes_location;
for (unsigned i = 0 ; i < axis_count; i++)
{
unsigned *axis_tag;
// only keep instances whose coordinates == pinned axis location
if (!c->plan->axes_old_index_tag_map->has (i, &axis_tag)) continue;
if (axes_location->has (*axis_tag) &&
fabsf (axes_location->get (*axis_tag) - coords[i].to_float ()) > 0.001f)
return_trace (false);
if (!c->plan->axes_index_map->has (i))
continue;
if (!c->serializer->embed (coords[i]))
return_trace (false);
}
if (has_postscript_nameid)
{
NameID name_id;
name_id = StructAfter<NameID> (coords);
if (!c->serializer->embed (name_id))
return_trace (false);
}
return_trace (true);
}
bool sanitize (hb_sanitize_context_t *c, unsigned int axis_count) const
{
TRACE_SANITIZE (this);
@ -58,7 +96,7 @@ struct InstanceRecord
NameID subfamilyNameID;/* The name ID for entries in the 'name' table
* that provide subfamily names for this instance. */
HBUINT16 flags; /* Reserved for future use — set to 0. */
UnsizedArrayOf<HBFixed>
UnsizedArrayOf<F16DOT16>
coordinatesZ; /* The coordinates array for this instance. */
//NameID postScriptNameIDX;/*Optional. The name ID for entries in the 'name'
// * table that provide PostScript names for this
@ -151,9 +189,9 @@ struct AxisRecord
public:
Tag axisTag; /* Tag identifying the design variation for the axis. */
protected:
HBFixed minValue; /* The minimum coordinate value for the axis. */
HBFixed defaultValue; /* The default coordinate value for the axis. */
HBFixed maxValue; /* The maximum coordinate value for the axis. */
F16DOT16 minValue; /* The minimum coordinate value for the axis. */
F16DOT16 defaultValue; /* The default coordinate value for the axis. */
F16DOT16 maxValue; /* The maximum coordinate value for the axis. */
public:
HBUINT16 flags; /* Axis flags. */
NameID axisNameID; /* The name ID for entries in the 'name' table that
@ -268,7 +306,7 @@ struct fvar
if (coords_length && *coords_length)
{
hb_array_t<const HBFixed> instanceCoords = instance->get_coordinates (axisCount)
hb_array_t<const F16DOT16> instanceCoords = instance->get_coordinates (axisCount)
.sub_array (0, coords_length);
for (unsigned int i = 0; i < instanceCoords.length; i++)
coords[i] = instanceCoords.arrayZ[i].to_float ();
@ -301,7 +339,7 @@ struct fvar
if (hb_any (+ hb_zip (instance->get_coordinates (axisCount), hb_range ((unsigned)axisCount))
| hb_filter (pinned_axes, hb_second)
| hb_map ([&] (const hb_pair_t<const HBFixed&, unsigned>& _)
| hb_map ([&] (const hb_pair_t<const F16DOT16&, unsigned>& _)
{
hb_tag_t axis_tag = pinned_axes.get (_.second);
float location = user_axes_location->get (axis_tag);
@ -321,6 +359,48 @@ struct fvar
}
}
bool subset (hb_subset_context_t *c) const
{
TRACE_SUBSET (this);
unsigned retained_axis_count = c->plan->axes_index_map->get_population ();
if (!retained_axis_count) //all axes are pinned
return_trace (false);
fvar *out = c->serializer->embed (this);
if (unlikely (!out)) return_trace (false);
if (!c->serializer->check_assign (out->axisCount, retained_axis_count, HB_SERIALIZE_ERROR_INT_OVERFLOW))
return_trace (false);
bool has_postscript_nameid = false;
if (instanceSize >= axisCount * 4 + 6)
has_postscript_nameid = true;
if (!c->serializer->check_assign (out->instanceSize, retained_axis_count * 4 + (has_postscript_nameid ? 6 : 4),
HB_SERIALIZE_ERROR_INT_OVERFLOW))
return_trace (false);
auto axes_records = get_axes ();
for (unsigned i = 0 ; i < (unsigned)axisCount; i++)
{
if (!c->plan->axes_index_map->has (i)) continue;
if (unlikely (!c->serializer->embed (axes_records[i])))
return_trace (false);
}
if (!c->serializer->check_assign (out->firstAxis, get_size (), HB_SERIALIZE_ERROR_INT_OVERFLOW))
return_trace (false);
for (unsigned i = 0 ; i < (unsigned)instanceCount; i++)
{
const InstanceRecord *instance = get_instance (i);
auto snap = c->serializer->snapshot ();
if (!instance->subset (c, axisCount, has_postscript_nameid))
c->serializer->revert (snap);
}
return_trace (true);
}
public:
hb_array_t<const AxisRecord> get_axes () const
{ return hb_array (&(this+firstAxis), axisCount); }
@ -346,8 +426,8 @@ struct fvar
HBUINT16 instanceCount; /* The number of named instances defined in the font
* (the number of records in the instances array). */
HBUINT16 instanceSize; /* The size in bytes of each InstanceRecord — set
* to either axisCount * sizeof(HBFixed) + 4, or to
* axisCount * sizeof(HBFixed) + 6. */
* to either axisCount * sizeof(F16DOT16) + 4, or to
* axisCount * sizeof(F16DOT16) + 6. */
public:
DEFINE_SIZE_STATIC (16);

View file

@ -244,7 +244,7 @@ bool _process_overflows (const hb_vector_t<graph::overflow_record_t>& overflows,
{
// The child object is shared, we may be able to eliminate the overflow
// by duplicating it.
if (!sorted_graph.duplicate (r.parent, r.child)) continue;
if (sorted_graph.duplicate (r.parent, r.child) == (unsigned) -1) continue;
return true;
}

View file

@ -0,0 +1,76 @@
/*
* Copyright © 2022 Google, Inc.
*
* This is part of HarfBuzz, a text shaping library.
*
* Permission is hereby granted, without written agreement and without
* license or royalty fees, to use, copy, modify, and distribute this
* software and its documentation for any purpose, provided that the
* above copyright notice and the following two paragraphs appear in
* all copies of this software.
*
* IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
* IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*
* THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
*
* Google Author(s): Garret Rieger
*/
#ifndef HB_SUBSET_ACCELERATOR_HH
#define HB_SUBSET_ACCELERATOR_HH
#include "hb.hh"
#include "hb-map.hh"
#include "hb-set.hh"
struct hb_subset_accelerator_t
{
static hb_user_data_key_t* user_data_key()
{
static hb_user_data_key_t key;
return &key;
}
static hb_subset_accelerator_t* create(const hb_map_t& unicode_to_gid_,
const hb_set_t& unicodes_) {
hb_subset_accelerator_t* accel =
(hb_subset_accelerator_t*) hb_malloc (sizeof(hb_subset_accelerator_t));
new (accel) hb_subset_accelerator_t (unicode_to_gid_, unicodes_);
return accel;
}
static void destroy(void* value) {
if (!value) return;
hb_subset_accelerator_t* accel = (hb_subset_accelerator_t*) value;
accel->~hb_subset_accelerator_t ();
hb_free (accel);
}
hb_subset_accelerator_t(const hb_map_t& unicode_to_gid_,
const hb_set_t& unicodes_)
: unicode_to_gid(unicode_to_gid_), unicodes(unicodes_) {}
const hb_map_t unicode_to_gid;
const hb_set_t unicodes;
// TODO(garretrieger): cumulative glyf checksum map
// TODO(garretrieger): sanitized table cache.
bool in_error () const
{
return unicode_to_gid.in_error() || unicodes.in_error ();
}
};
#endif /* HB_SUBSET_ACCELERATOR_HH */

View file

@ -49,7 +49,7 @@ hb_subset_input_create_or_fail (void)
set = hb_set_create ();
input->axes_location = hb_hashmap_create<hb_tag_t, float> ();
if (!input->axes_location || input->in_error ())
{
hb_subset_input_destroy (input);
@ -89,7 +89,6 @@ hb_subset_input_create_or_fail (void)
hb_tag_t default_no_subset_tables[] = {
HB_TAG ('a', 'v', 'a', 'r'),
HB_TAG ('f', 'v', 'a', 'r'),
HB_TAG ('g', 'a', 's', 'p'),
HB_TAG ('c', 'v', 't', ' '),
HB_TAG ('f', 'p', 'g', 'm'),
@ -393,7 +392,7 @@ hb_subset_input_get_user_data (const hb_subset_input_t *input,
*
* Since: EXPERIMENTAL
**/
hb_bool_t
HB_EXTERN hb_bool_t
hb_subset_input_pin_axis_to_default (hb_subset_input_t *input,
hb_face_t *face,
hb_tag_t axis_tag)
@ -417,7 +416,7 @@ hb_subset_input_pin_axis_to_default (hb_subset_input_t *input,
*
* Since: EXPERIMENTAL
**/
hb_bool_t
HB_EXTERN hb_bool_t
hb_subset_input_pin_axis_location (hb_subset_input_t *input,
hb_face_t *face,
hb_tag_t axis_tag,
@ -432,3 +431,51 @@ hb_subset_input_pin_axis_location (hb_subset_input_t *input,
}
#endif
#endif
#ifdef HB_EXPERIMENTAL_API
/**
* hb_subset_preprocess
* @input: a #hb_face_t object.
*
* Preprocesses the face and attaches data that will be needed by the
* subsetter. Future subsetting operations can then use the precomputed data
* to speed up the subsetting operation.
*
* Since: EXPERIMENTAL
**/
HB_EXTERN hb_face_t *
hb_subset_preprocess (hb_face_t *source)
{
hb_subset_input_t* input = hb_subset_input_create_or_fail ();
hb_set_clear (hb_subset_input_set(input, HB_SUBSET_SETS_UNICODE));
hb_set_invert (hb_subset_input_set(input, HB_SUBSET_SETS_UNICODE));
hb_set_clear (hb_subset_input_set(input,
HB_SUBSET_SETS_LAYOUT_FEATURE_TAG));
hb_set_invert (hb_subset_input_set(input,
HB_SUBSET_SETS_LAYOUT_FEATURE_TAG));
hb_set_clear (hb_subset_input_set(input,
HB_SUBSET_SETS_LAYOUT_SCRIPT_TAG));
hb_set_invert (hb_subset_input_set(input,
HB_SUBSET_SETS_LAYOUT_SCRIPT_TAG));
hb_set_clear (hb_subset_input_set(input,
HB_SUBSET_SETS_NAME_ID));
hb_set_invert (hb_subset_input_set(input,
HB_SUBSET_SETS_NAME_ID));
hb_subset_input_set_flags(input,
HB_SUBSET_FLAGS_NOTDEF_OUTLINE |
HB_SUBSET_FLAGS_GLYPH_NAMES |
HB_SUBSET_FLAGS_RETAIN_GIDS);
input->attach_accelerator_data = true;
hb_face_t* new_source = hb_subset_or_fail (source, input);
hb_subset_input_destroy (input);
return new_source;
}
#endif

View file

@ -59,6 +59,7 @@ struct hb_subset_input_t
};
unsigned flags;
bool attach_accelerator_data = false;
hb_hashmap_t<hb_tag_t, float> *axes_location;
inline unsigned num_sets () const

View file

@ -25,6 +25,7 @@
*/
#include "hb-subset-plan.hh"
#include "hb-subset-accelerator.hh"
#include "hb-map.hh"
#include "hb-set.hh"
@ -129,7 +130,9 @@ template <typename T>
static void _collect_layout_indices (hb_subset_plan_t *plan,
const T& table,
hb_set_t *lookup_indices, /* OUT */
hb_set_t *feature_indices /* OUT */)
hb_set_t *feature_indices, /* OUT */
hb_hashmap_t<unsigned, hb::shared_ptr<hb_set_t>> *feature_record_cond_idx_map, /* OUT */
hb_hashmap_t<unsigned, const OT::Feature*> *feature_substitutes_map /* OUT */)
{
unsigned num_features = table.get_feature_count ();
hb_vector_t<hb_tag_t> features;
@ -154,16 +157,37 @@ static void _collect_layout_indices (hb_subset_plan_t *plan,
retain_all_features ? nullptr : features.arrayZ,
feature_indices);
#ifndef HB_NO_VAR
// collect feature substitutes with variations
if (!plan->user_axes_location->is_empty ())
{
hb_hashmap_t<hb::shared_ptr<hb_map_t>, unsigned> conditionset_map;
OT::hb_collect_feature_substitutes_with_var_context_t c =
{
plan->axes_old_index_tag_map,
plan->axes_location,
feature_record_cond_idx_map,
feature_substitutes_map,
feature_indices,
true,
0,
&conditionset_map
};
table.collect_feature_substitutes_with_variations (&c);
}
#endif
for (unsigned feature_index : *feature_indices)
{
//TODO: replace HB_OT_LAYOUT_NO_VARIATIONS_INDEX with variation_index for
//instancing
const OT::Feature &f = table.get_feature_variation (feature_index, HB_OT_LAYOUT_NO_VARIATIONS_INDEX);
f.add_lookup_indexes_to (lookup_indices);
const OT::Feature* f = &(table.get_feature (feature_index));
const OT::Feature **p = nullptr;
if (feature_substitutes_map->has (feature_index, &p))
f = *p;
f->add_lookup_indexes_to (lookup_indices);
}
//TODO: update for instancing: only collect lookups from feature_indexes that have no variations
table.feature_variation_collect_lookups (feature_indices, lookup_indices);
table.feature_variation_collect_lookups (feature_indices, feature_substitutes_map, lookup_indices);
}
@ -171,6 +195,7 @@ static inline void
_GSUBGPOS_find_duplicate_features (const OT::GSUBGPOS &g,
const hb_map_t *lookup_indices,
const hb_set_t *feature_indices,
const hb_hashmap_t<unsigned, const OT::Feature*> *feature_substitutes_map,
hb_map_t *duplicate_feature_map /* OUT */)
{
if (feature_indices->is_empty ()) return;
@ -195,16 +220,22 @@ _GSUBGPOS_find_duplicate_features (const OT::GSUBGPOS &g,
hb_set_t* same_tag_features = unique_features.get (t);
for (unsigned other_f_index : same_tag_features->iter ())
{
const OT::Feature& f = g.get_feature (i);
const OT::Feature& other_f = g.get_feature (other_f_index);
const OT::Feature* f = &(g.get_feature (i));
const OT::Feature **p = nullptr;
if (feature_substitutes_map->has (i, &p))
f = *p;
const OT::Feature* other_f = &(g.get_feature (other_f_index));
if (feature_substitutes_map->has (other_f_index, &p))
f = *p;
auto f_iter =
+ hb_iter (f.lookupIndex)
+ hb_iter (f->lookupIndex)
| hb_filter (lookup_indices)
;
auto other_f_iter =
+ hb_iter (other_f.lookupIndex)
+ hb_iter (other_f->lookupIndex)
| hb_filter (lookup_indices)
;
@ -237,7 +268,9 @@ _closure_glyphs_lookups_features (hb_subset_plan_t *plan,
hb_set_t *gids_to_retain,
hb_map_t *lookups,
hb_map_t *features,
script_langsys_map *langsys_map)
script_langsys_map *langsys_map,
hb_hashmap_t<unsigned, hb::shared_ptr<hb_set_t>> *feature_record_cond_idx_map,
hb_hashmap_t<unsigned, const OT::Feature*> *feature_substitutes_map)
{
hb_blob_ptr_t<T> table = plan->source_table<T> ();
hb_tag_t table_tag = table->tableTag;
@ -245,7 +278,9 @@ _closure_glyphs_lookups_features (hb_subset_plan_t *plan,
_collect_layout_indices<T> (plan,
*table,
&lookup_indices,
&feature_indices);
&feature_indices,
feature_record_cond_idx_map,
feature_substitutes_map);
if (table_tag == HB_OT_TAG_GSUB)
hb_ot_layout_lookups_substitute_closure (plan->source,
@ -257,9 +292,12 @@ _closure_glyphs_lookups_features (hb_subset_plan_t *plan,
_remap_indexes (&lookup_indices, lookups);
// prune features
table->prune_features (lookups, &feature_indices);
table->prune_features (lookups,
plan->user_axes_location->is_empty () ? nullptr : feature_record_cond_idx_map,
feature_substitutes_map,
&feature_indices);
hb_map_t duplicate_feature_map;
_GSUBGPOS_find_duplicate_features (*table, lookups, &feature_indices, &duplicate_feature_map);
_GSUBGPOS_find_duplicate_features (*table, lookups, &feature_indices, feature_substitutes_map, &duplicate_feature_map);
feature_indices.clear ();
table->prune_langsys (&duplicate_feature_map, plan->layout_scripts, langsys_map, &feature_indices);
@ -419,41 +457,73 @@ _populate_unicodes_to_retain (const hb_set_t *unicodes,
hb_subset_plan_t *plan)
{
OT::cmap::accelerator_t cmap (plan->source);
unsigned size_threshold = plan->source->get_num_glyphs ();
if (glyphs->is_empty () && unicodes->get_population () < size_threshold)
{
const hb_map_t* unicode_to_gid = nullptr;
if (plan->accelerator)
unicode_to_gid = &plan->accelerator->unicode_to_gid;
// This is approach to collection is faster, but can only be used if glyphs
// are not being explicitly added to the subset and the input unicodes set is
// not excessively large (eg. an inverted set).
plan->unicode_to_new_gid_list.alloc (unicodes->get_population ());
for (hb_codepoint_t cp : *unicodes)
{
hb_codepoint_t gid;
if (!cmap.get_nominal_glyph (cp, &gid))
if (!unicode_to_gid) {
for (hb_codepoint_t cp : *unicodes)
{
DEBUG_MSG(SUBSET, nullptr, "Drop U+%04X; no gid", cp);
continue;
}
hb_codepoint_t gid;
if (!cmap.get_nominal_glyph (cp, &gid))
{
DEBUG_MSG(SUBSET, nullptr, "Drop U+%04X; no gid", cp);
continue;
}
plan->codepoint_to_glyph->set (cp, gid);
plan->unicode_to_new_gid_list.push (hb_pair (cp, gid));
plan->codepoint_to_glyph->set (cp, gid);
plan->unicode_to_new_gid_list.push (hb_pair (cp, gid));
}
} else {
// Use in memory unicode to gid map it's faster then looking up from
// the map. This code is mostly duplicated from above to avoid doing
// conditionals on the presence of the unicode_to_gid map each
// iteration.
for (hb_codepoint_t cp : *unicodes)
{
hb_codepoint_t gid = unicode_to_gid->get (cp);
if (gid == HB_MAP_VALUE_INVALID)
{
DEBUG_MSG(SUBSET, nullptr, "Drop U+%04X; no gid", cp);
continue;
}
plan->codepoint_to_glyph->set (cp, gid);
plan->unicode_to_new_gid_list.push (hb_pair (cp, gid));
}
}
}
else
{
// This approach is slower, but can handle adding in glyphs to the subset and will match
// them with cmap entries.
hb_map_t unicode_glyphid_map;
hb_set_t cmap_unicodes;
cmap.collect_mapping (&cmap_unicodes, &unicode_glyphid_map);
plan->unicode_to_new_gid_list.alloc (hb_min(unicodes->get_population ()
+ glyphs->get_population (),
cmap_unicodes.get_population ()));
for (hb_codepoint_t cp : cmap_unicodes)
hb_map_t unicode_glyphid_map_storage;
hb_set_t cmap_unicodes_storage;
const hb_map_t* unicode_glyphid_map = &unicode_glyphid_map_storage;
const hb_set_t* cmap_unicodes = &cmap_unicodes_storage;
if (!plan->accelerator) {
cmap.collect_mapping (&cmap_unicodes_storage, &unicode_glyphid_map_storage);
plan->unicode_to_new_gid_list.alloc (hb_min(unicodes->get_population ()
+ glyphs->get_population (),
cmap_unicodes->get_population ()));
} else {
unicode_glyphid_map = &plan->accelerator->unicode_to_gid;
cmap_unicodes = &plan->accelerator->unicodes;
}
for (hb_codepoint_t cp : *cmap_unicodes)
{
hb_codepoint_t gid = unicode_glyphid_map[cp];
hb_codepoint_t gid = (*unicode_glyphid_map)[cp];
if (!unicodes->has (cp) && !glyphs->has (gid))
continue;
@ -509,9 +579,7 @@ _glyf_add_gid_and_children (const OT::glyf_accelerator_t &glyf,
static void
_populate_gids_to_retain (hb_subset_plan_t* plan,
bool close_over_gsub,
bool close_over_gpos,
bool close_over_gdef)
hb_set_t* drop_tables)
{
OT::glyf_accelerator_t glyf (plan->source);
#ifndef HB_NO_SUBSET_CFF
@ -523,32 +591,42 @@ _populate_gids_to_retain (hb_subset_plan_t* plan,
_cmap_closure (plan->source, plan->unicodes, plan->_glyphset_gsub);
#ifndef HB_NO_SUBSET_LAYOUT
if (close_over_gsub)
if (!drop_tables->has (HB_OT_TAG_GSUB))
// closure all glyphs/lookups/features needed for GSUB substitutions.
_closure_glyphs_lookups_features<GSUB> (
plan,
plan->_glyphset_gsub,
plan->gsub_lookups,
plan->gsub_features,
plan->gsub_langsys);
plan->gsub_langsys,
plan->gsub_feature_record_cond_idx_map,
plan->gsub_feature_substitutes_map);
if (close_over_gpos)
if (!drop_tables->has (HB_OT_TAG_GPOS))
_closure_glyphs_lookups_features<GPOS> (
plan,
plan->_glyphset_gsub,
plan->gpos_lookups,
plan->gpos_features,
plan->gpos_langsys);
plan->gpos_langsys,
plan->gpos_feature_record_cond_idx_map,
plan->gpos_feature_substitutes_map);
#endif
_remove_invalid_gids (plan->_glyphset_gsub, plan->source->get_num_glyphs ());
hb_set_set (plan->_glyphset_mathed, plan->_glyphset_gsub);
_math_closure (plan, plan->_glyphset_mathed);
_remove_invalid_gids (plan->_glyphset_mathed, plan->source->get_num_glyphs ());
if (!drop_tables->has (HB_OT_TAG_MATH))
{
_math_closure (plan, plan->_glyphset_mathed);
_remove_invalid_gids (plan->_glyphset_mathed, plan->source->get_num_glyphs ());
}
hb_set_t cur_glyphset = *plan->_glyphset_mathed;
_colr_closure (plan->source, plan->colrv1_layers, plan->colr_palettes, &cur_glyphset);
_remove_invalid_gids (&cur_glyphset, plan->source->get_num_glyphs ());
if (!drop_tables->has (HB_OT_TAG_COLR))
{
_colr_closure (plan->source, plan->colrv1_layers, plan->colr_palettes, &cur_glyphset);
_remove_invalid_gids (&cur_glyphset, plan->source->get_num_glyphs ());
}
hb_set_set (plan->_glyphset_colred, &cur_glyphset);
@ -570,7 +648,7 @@ _populate_gids_to_retain (hb_subset_plan_t* plan,
#ifndef HB_NO_VAR
if (close_over_gdef)
if (!drop_tables->has (HB_OT_TAG_GDEF))
_collect_layout_variation_indices (plan);
#endif
}
@ -659,18 +737,22 @@ _normalize_axes_location (hb_face_t *face, hb_subset_plan_t *plan)
seg_maps = face->table.avar->get_segment_maps ();
bool axis_not_pinned = false;
unsigned axis_count = 0;
unsigned old_axis_idx = 0, new_axis_idx = 0;
for (const auto& axis : axes)
{
hb_tag_t axis_tag = axis.get_axis_tag ();
plan->axes_old_index_tag_map->set (old_axis_idx, axis_tag);
if (!plan->user_axes_location->has (axis_tag))
{
axis_not_pinned = true;
plan->axes_index_map->set (old_axis_idx, new_axis_idx);
new_axis_idx++;
}
else
{
int normalized_v = axis.normalize_axis_value (plan->user_axes_location->get (axis_tag));
if (has_avar && axis_count < face->table.avar->get_axis_count ())
if (has_avar && old_axis_idx < face->table.avar->get_axis_count ())
{
normalized_v = seg_maps->map (normalized_v);
}
@ -681,7 +763,7 @@ _normalize_axes_location (hb_face_t *face, hb_subset_plan_t *plan)
if (has_avar)
seg_maps = &StructAfter<OT::SegmentMaps> (*seg_maps);
axis_count++;
old_axis_idx++;
}
plan->all_axes_pinned = !axis_not_pinned;
}
@ -741,6 +823,13 @@ hb_subset_plan_create_or_fail (hb_face_t *face,
plan->gsub_features = hb_map_create ();
plan->gpos_features = hb_map_create ();
plan->check_success (plan->gsub_feature_record_cond_idx_map = hb_hashmap_create<unsigned, hb::shared_ptr<hb_set_t>> ());
plan->check_success (plan->gpos_feature_record_cond_idx_map = hb_hashmap_create<unsigned, hb::shared_ptr<hb_set_t>> ());
plan->check_success (plan->gsub_feature_substitutes_map = hb_hashmap_create<unsigned, const OT::Feature*> ());
plan->check_success (plan->gpos_feature_substitutes_map = hb_hashmap_create<unsigned, const OT::Feature*> ());
plan->colrv1_layers = hb_map_create ();
plan->colr_palettes = hb_map_create ();
plan->check_success (plan->layout_variation_idx_delta_map = hb_hashmap_create<unsigned, hb_pair_t<unsigned, int>> ());
@ -751,12 +840,21 @@ hb_subset_plan_create_or_fail (hb_face_t *face,
plan->check_success (plan->user_axes_location = hb_hashmap_create<hb_tag_t, float> ());
if (plan->user_axes_location && input->axes_location)
*plan->user_axes_location = *input->axes_location;
plan->check_success (plan->axes_index_map = hb_map_create ());
plan->check_success (plan->axes_old_index_tag_map = hb_map_create ());
plan->all_axes_pinned = false;
plan->pinned_at_default = true;
plan->check_success (plan->vmtx_map = hb_hashmap_create<unsigned, hb_pair_t<unsigned, int>> ());
plan->check_success (plan->hmtx_map = hb_hashmap_create<unsigned, hb_pair_t<unsigned, int>> ());
void* accel = hb_face_get_user_data(face, hb_subset_accelerator_t::user_data_key());
plan->attach_accelerator_data = input->attach_accelerator_data;
if (accel)
plan->accelerator = (hb_subset_accelerator_t*) accel;
if (unlikely (plan->in_error ())) {
hb_subset_plan_destroy (plan);
return nullptr;
@ -768,10 +866,7 @@ hb_subset_plan_create_or_fail (hb_face_t *face,
_populate_unicodes_to_retain (input->sets.unicodes, input->sets.glyphs, plan);
_populate_gids_to_retain (plan,
!input->sets.drop_tables->has (HB_OT_TAG_GSUB),
!input->sets.drop_tables->has (HB_OT_TAG_GPOS),
!input->sets.drop_tables->has (HB_OT_TAG_GDEF));
_populate_gids_to_retain (plan, input->sets.drop_tables);
_create_old_gid_to_new_gid_map (face,
input->flags & HB_SUBSET_FLAGS_RETAIN_GIDS,

View file

@ -31,11 +31,16 @@
#include "hb-subset.h"
#include "hb-subset-input.hh"
#include "hb-subset-accelerator.hh"
#include "hb-map.hh"
#include "hb-bimap.hh"
#include "hb-set.hh"
namespace OT {
struct Feature;
}
struct hb_subset_plan_t
{
hb_subset_plan_t ()
@ -67,9 +72,15 @@ struct hb_subset_plan_t
hb_map_destroy (gpos_features);
hb_map_destroy (colrv1_layers);
hb_map_destroy (colr_palettes);
hb_map_destroy (axes_index_map);
hb_map_destroy (axes_old_index_tag_map);
hb_hashmap_destroy (gsub_langsys);
hb_hashmap_destroy (gpos_langsys);
hb_hashmap_destroy (gsub_feature_record_cond_idx_map);
hb_hashmap_destroy (gpos_feature_record_cond_idx_map);
hb_hashmap_destroy (gsub_feature_substitutes_map);
hb_hashmap_destroy (gpos_feature_substitutes_map);
hb_hashmap_destroy (axes_location);
hb_hashmap_destroy (sanitized_table_cache);
hb_hashmap_destroy (hmtx_map);
@ -87,6 +98,7 @@ struct hb_subset_plan_t
bool successful;
unsigned flags;
bool attach_accelerator_data = false;
// For each cp that we'd like to retain maps to the corresponding gid.
hb_set_t *unicodes;
@ -143,6 +155,15 @@ struct hb_subset_plan_t
hb_map_t *gsub_features;
hb_map_t *gpos_features;
//active feature variation records/condition index with variations
hb_hashmap_t<unsigned, hb::shared_ptr<hb_set_t>> *gsub_feature_record_cond_idx_map;
hb_hashmap_t<unsigned, hb::shared_ptr<hb_set_t>> *gpos_feature_record_cond_idx_map;
//feature index-> address of substituation feature table mapping with
//variations
hb_hashmap_t<unsigned, const OT::Feature*> *gsub_feature_substitutes_map;
hb_hashmap_t<unsigned, const OT::Feature*> *gpos_feature_substitutes_map;
//active layers/palettes we'd like to retain
hb_map_t *colrv1_layers;
hb_map_t *colr_palettes;
@ -158,6 +179,10 @@ struct hb_subset_plan_t
hb_hashmap_t<hb_tag_t, int> *axes_location;
//user specified axes location map
hb_hashmap_t<hb_tag_t, float> *user_axes_location;
//retained old axis index -> new axis index mapping in fvar axis array
hb_map_t *axes_index_map;
//axis_index->axis_tag mapping in fvar axis array
hb_map_t *axes_old_index_tag_map;
bool all_axes_pinned;
bool pinned_at_default;
@ -166,6 +191,8 @@ struct hb_subset_plan_t
//vmtx metrics map: new gid->(advance, lsb)
hb_hashmap_t<unsigned, hb_pair_t<unsigned, int>> *vmtx_map;
const hb_subset_accelerator_t* accelerator;
public:
template<typename T>

View file

@ -50,11 +50,13 @@
#include "hb-ot-color-cbdt-table.hh"
#include "hb-ot-layout-gsub-table.hh"
#include "hb-ot-layout-gpos-table.hh"
#include "hb-ot-var-fvar-table.hh"
#include "hb-ot-var-gvar-table.hh"
#include "hb-ot-var-hvar-table.hh"
#include "hb-ot-math-table.hh"
#include "hb-ot-stat-table.hh"
#include "hb-repacker.hh"
#include "hb-subset-accelerator.hh"
using OT::Layout::GSUB;
using OT::Layout::GPOS;
@ -475,6 +477,9 @@ _subset_table (hb_subset_plan_t *plan,
case HB_OT_TAG_HVAR: return _subset<const OT::HVAR> (plan, buf);
case HB_OT_TAG_VVAR: return _subset<const OT::VVAR> (plan, buf);
#endif
case HB_OT_TAG_fvar:
if (plan->user_axes_location->is_empty ()) return _passthrough (plan, tag);
return _subset<const OT::fvar> (plan, buf);
case HB_OT_TAG_STAT:
/*TODO(qxliu): change the condition as we support more complex
* instancing operation*/
@ -490,6 +495,27 @@ _subset_table (hb_subset_plan_t *plan,
}
}
static void _attach_accelerator_data (const hb_subset_plan_t* plan,
hb_face_t* face /* IN/OUT */)
{
hb_subset_accelerator_t* accel =
hb_subset_accelerator_t::create (*plan->codepoint_to_glyph,
*plan->unicodes);
if (accel->in_error ())
{
hb_subset_accelerator_t::destroy (accel);
return;
}
if (!hb_face_set_user_data(face,
hb_subset_accelerator_t::user_data_key(),
accel,
hb_subset_accelerator_t::destroy,
true))
hb_subset_accelerator_t::destroy (accel);
}
/**
* hb_subset_or_fail:
* @source: font face data to be subset.
@ -572,6 +598,10 @@ hb_subset_plan_execute_or_fail (hb_subset_plan_t *plan)
offset += num_tables;
}
if (success && plan->attach_accelerator_data) {
_attach_accelerator_data (plan, plan->dest);
}
end:
return success ? hb_face_reference (plan->dest) : nullptr;
}

View file

@ -70,6 +70,14 @@ typedef struct hb_subset_plan_t hb_subset_plan_t;
* in the final subset.
* @HB_SUBSET_FLAGS_NO_PRUNE_UNICODE_RANGES: If set then the unicode ranges in
* OS/2 will not be recalculated.
* @HB_SUBSET_FLAGS_PATCH_MODE: If set the subsetter behaviour will be modified
* to produce a subset that is better suited to patching. For example cmap
* subtable format will be kept stable.
* @HB_SUBSET_FLAGS_OMIT_GLYF: If set the subsetter won't actually produce the final
* glyf table bytes. The table directory will include and entry as if the table was
* there but the actual final font blob will be truncated prior to the glyf data. This
* is a useful performance optimization when a font aware binary patching algorithm
* is being used to diff two subsets.
*
* List of boolean properties that can be configured on the subset input.
*
@ -86,6 +94,8 @@ typedef enum { /*< flags >*/
HB_SUBSET_FLAGS_NOTDEF_OUTLINE = 0x00000040u,
HB_SUBSET_FLAGS_GLYPH_NAMES = 0x00000080u,
HB_SUBSET_FLAGS_NO_PRUNE_UNICODE_RANGES = 0x00000100u,
// Not supported yet: HB_SUBSET_FLAGS_PATCH_MODE = 0x00000200u,
// Not supported yet: HB_SUBSET_FLAGS_OMIT_GLYF = 0x00000400u,
} hb_subset_flags_t;
/**
@ -169,6 +179,13 @@ hb_subset_input_pin_axis_location (hb_subset_input_t *input,
#endif
#endif
#ifdef HB_EXPERIMENTAL_API
HB_EXTERN hb_face_t *
hb_subset_preprocess (hb_face_t *source);
#endif
HB_EXTERN hb_face_t *
hb_subset_or_fail (hb_face_t *source, const hb_subset_input_t *input);

View file

@ -47,20 +47,20 @@ HB_BEGIN_DECLS
*
* The minor component of the library version available at compile-time.
*/
#define HB_VERSION_MINOR 2
#define HB_VERSION_MINOR 3
/**
* HB_VERSION_MICRO:
*
* The micro component of the library version available at compile-time.
*/
#define HB_VERSION_MICRO 0
#define HB_VERSION_MICRO 1
/**
* HB_VERSION_STRING:
*
* A string literal containing the library version available at compile-time.
*/
#define HB_VERSION_STRING "5.2.0"
#define HB_VERSION_STRING "5.3.1"
/**
* HB_VERSION_ATLEAST: