for (unsigned i = 0; i < map.length; i++) {
map[i] = (unsigned) -1;
}
for (constauto& l : liga_set.vertex->obj.real_links) { if (l.position < 2) continue; unsigned array_index = (l.position - 2) / 2;
map[array_index] = l.objidx;
} return map;
}
hb_vector_t<unsigned> compute_split_points(gsubgpos_graph_context_t& c, unsigned this_index) const
{ // For ligature subst coverage is always packed last, and as a result is where an overflow // will happen if there is one, so we can check the estimate length of the // LigatureSubstFormat1 -> Coverage offset length which is the sum of all data in the // retained sub graph except for the coverage table itself. constunsigned base_size = OT::Layout::GSUB_impl::LigatureSubstFormat1_2<SmallTypes>::min_size; unsigned accumulated = base_size;
unsigned ligature_index = 0;
hb_vector_t<unsigned> split_points; for (unsigned i = 0; i < ligatureSet.len; i++)
{
accumulated += OT::HBUINT16::static_size; // for ligature set offset
accumulated += OT::Layout::GSUB_impl::LigatureSet<SmallTypes>::min_size; // for ligature set table
auto liga_set = c.graph.as_table<LigatureSet>(this_index, &ligatureSet[i]); if (!liga_set.table) { return hb_vector_t<unsigned> {};
}
// Finding the object id associated with an array index is O(n) // so to avoid O(n^2), precompute the mapping by scanning through // all links auto index_to_id = ligature_index_to_object_id(liga_set); if (index_to_id.in_error()) return hb_vector_t<unsigned>();
for (unsigned j = 0; j < liga_set.table->ligature.len; j++)
{ constunsigned liga_id = index_to_id[j]; if (liga_id == (unsigned) -1) continue; // no outgoing link, ignore constunsigned liga_size = c.graph.vertices_[liga_id].table_size ();
accumulated += OT::HBUINT16::static_size; // for ligature offset
accumulated += liga_size; // for the ligature table
if (accumulated >= (1 << 16))
{
split_points.push(ligature_index); // We're going to split such that the current ligature will be in the new sub table. // That means we'll have one ligature subst (base_base), one ligature set, and one liga table
accumulated = base_size + // for liga subst subtable
(OT::HBUINT16::static_size * 2) + // for liga set and liga offset
OT::Layout::GSUB_impl::LigatureSet<SmallTypes>::min_size + // for liga set subtable
liga_size; // for liga sub table
}
hb_pair_t<unsigned, unsigned> current_liga_set_bounds (gsubgpos_graph_context_t& c, unsigned liga_set_index, const hb_serialize_context_t::object_t& liga_set) const
{ // Finds the actual liga indices present in the liga set currently. Takes // into account those that have been removed by processing. unsigned min_index = (unsigned) -1; unsigned max_index = 0; for (constauto& l : liga_set.real_links) { if (l.position < 2) continue;
// compact the remaining linked liga offsets into a continous array and shrink the node as needed. unsigned to_remove = table->ligature.len - obj.real_links.length; unsigned new_position = SmallTypes::size;
obj.real_links.qsort(); // for this to work we need to process links in order of position. for (auto& l : obj.real_links)
{
l.position = new_position;
new_position += SmallTypes::size;
}
// Create an oversized new liga subst, we'll adjust the size down later. We don't know // the final size until we process it but we also need it to exist while we're processing // so that nodes can be moved to it as needed. unsigned prime_size = OT::Layout::GSUB_impl::LigatureSubstFormat1_2<SmallTypes>::min_size
+ ligatureSet.get_size() - ligatureSet.len.get_size();
// Create a place holder coverage prime id since we need to add virtual links to it while // generating liga and liga sets. Afterwards it will be updated to have the correct coverage. unsigned coverage_id = c.graph.index_for_offset (this_index, &coverage); unsigned coverage_prime_id = c.graph.duplicate(coverage_id); auto& coverage_prime_vertex = c.graph.vertices_[coverage_prime_id]; auto* coverage_prime_link = c.graph.vertices_[liga_subst_prime_id].obj.real_links.push ();
coverage_prime_link->width = SmallTypes::size;
coverage_prime_link->objidx = coverage_prime_id;
coverage_prime_link->position = 2;
coverage_prime_vertex.add_parent (liga_subst_prime_id, false);
// Locate all liga sets with ligas between start and end. // Clone or move them as needed. unsigned count = 0; unsigned liga_set_count = 0; unsigned liga_set_start = -1; unsigned liga_set_end = 0; // inclusive for (unsigned i = 0; i < liga_counts.length; i++)
{ unsigned num_ligas = liga_counts[i];
if (current_start >= end || start >= current_end) { // No intersection, so just skip
count += num_ligas; continue;
}
auto liga_set_index = c.graph.index_for_offset(this_index, &ligatureSet[i]); auto liga_set = c.graph.as_table<LigatureSet>(this_index, &ligatureSet[i]); if (!liga_set.table) { return -1;
}
// Bounds may need to be adjusted if some ligas have been previously removed.
hb_pair_t<unsigned, unsigned> liga_bounds = current_liga_set_bounds(c, liga_set_index, liga_set.vertex->obj);
current_start = hb_max(count + liga_bounds.first, current_start);
current_end = hb_min(count + liga_bounds.second, current_end);
unsigned liga_set_prime_id; if (current_start >= start && current_end <= end) { // This liga set is fully contined within [start, end) // We can move the entire ligaset to the new liga subset object.
liga_set_end = i; if (i < liga_set_start) liga_set_start = i;
liga_set_prime_id = c.graph.move_child<> (this_index,
&ligatureSet[i],
liga_subst_prime_id,
&liga_subst_prime->ligatureSet[liga_set_count++]);
compact_liga_set(c, liga_set.table, liga_set.vertex->obj);
} else
{ // This liga set partially overlaps [start, end). We'll need to create // a new liga set sub table and move the intersecting ligas to it. unsigned start_index = hb_max(start, current_start) - count; unsigned end_index = hb_min(end, current_end) - count; unsigned liga_count = end_index - start_index; auto result = new_liga_set(c, liga_count);
liga_set_prime_id = result.first; if (liga_set_prime_id == (unsigned) -1) return -1;
liga_set_end = i; if (i < liga_set_start) liga_set_start = i;
c.graph.add_link(&liga_subst_prime->ligatureSet[liga_set_count++], liga_subst_prime_id, liga_set_prime_id);
}
// The new liga and all children set needs to have a virtual link to the new coverage table: auto& liga_set_prime = c.graph.vertices_[liga_set_prime_id].obj;
clear_virtual_links(c, liga_set_prime_id);
add_virtual_link(c, liga_set_prime_id, coverage_prime_id); for (constauto& l : liga_set_prime.real_links) {
clear_virtual_links(c, l.objidx);
add_virtual_link(c, l.objidx, coverage_prime_id);
}
hb_set_t retained_indices; unsigned new_liga_set_count = 0; for (unsigned i = 0; i < liga_counts.length; i++)
{ auto liga_set = c.graph.as_table<LigatureSet>(this_index, &ligatureSet[i]); if (!liga_set.table) { returnfalse;
}
// We need the virtual links to coverage removed from all descendants on this liga subst. // If any are left when we try to mutate the coverage table later it will be unnessecarily // duplicated. Code later on will re-add the virtual links as needed (via retained_indices).
clear_virtual_links(c, liga_set.index);
retained_indices.add(liga_set.index);
auto index_to_id = ligature_index_to_object_id(liga_set); if (index_to_id.in_error()) returnfalse;
for (unsigned i = 0; i < liga_set.table->ligature.len; i++) { unsigned liga_index = index_to_id[i]; if (liga_index != (unsigned) -1) {
clear_virtual_links(c, liga_index);
retained_indices.add(liga_index);
}
}
unsigned num_ligas = liga_counts[i]; if (num_ligas >= count) { // drop the trailing liga's from this set and all subsequent liga sets unsigned num_ligas_to_remove = num_ligas - count;
new_liga_set_count = i + 1;
c.graph.vertices_[liga_set.index].obj.tail -= num_ligas_to_remove * SmallTypes::size;
liga_set.table->ligature.len = count; break;
} else {
count -= num_ligas;
}
}
// Adjust liga set array auto& this_vertex = c.graph.vertices_[this_index];
this_vertex.obj.tail -= (ligatureSet.len - new_liga_set_count) * SmallTypes::size;
ligatureSet.len = new_liga_set_count;
// Coverage matches the number of liga sets so rebuild as needed unsigned coverage_idx = c.graph.index_for_offset (this_index, &this->coverage); if (coverage_idx == (unsigned) -1) returnfalse;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.