/* * Removes all tags from 'tags' that are not in filter. Additionally eliminates any duplicates. * Returns true if anything was removed (not including duplicates).
*/ staticbool _filter_tag_list(hb_vector_t<hb_tag_t>* tags, /* IN/OUT */ const hb_set_t* filter)
{
hb_vector_t<hb_tag_t> out;
out.alloc (tags->get_size() + 1); // +1 is to allocate room for the null terminator.
bool removed = false;
hb_set_t visited;
for (hb_tag_t tag : *tags)
{ if (!tag) continue; if (visited.has (tag)) continue;
if (!filter->has (tag))
{
removed = true; continue;
}
visited.add (tag);
out.push (tag);
}
// The collect function needs a null element to signal end of the array.
out.push (HB_TAG_NONE);
hb_swap (out, *tags); return removed;
}
template <typename T> staticvoid _collect_layout_indices (hb_subset_plan_t *plan, const T& table,
hb_set_t *lookup_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 */
hb_set_t& catch_all_record_feature_idxes, /* OUT */
hb_hashmap_t<unsigned, hb_pair_t<constvoid*, constvoid*>>& catch_all_record_idx_feature_map /* OUT */)
{ unsigned num_features = table.get_feature_count ();
hb_vector_t<hb_tag_t> features; if (!plan->check_success (features.resize (num_features))) return;
table.get_feature_tags (0, &num_features, features.arrayZ); bool retain_all_features = !_filter_tag_list (&features, &plan->layout_features);
// If all axes are pinned then all feature variations will be dropped so there's no need // to collect lookups from them. if (!plan->all_axes_pinned)
table.feature_variation_collect_lookups (feature_indices,
plan->user_axes_location.is_empty () ? nullptr: feature_record_cond_idx_map,
lookup_indices); #endif
}
staticinlinevoid
_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;
hb_hashmap_t<hb_tag_t, hb::unique_ptr<hb_set_t>> unique_features; //find out duplicate features after subset for (unsigned i : feature_indices->iter ())
{
hb_tag_t t = g.get_feature_tag (i); if (t == HB_MAP_VALUE_INVALID) continue; if (!unique_features.has (t))
{ if (unlikely (!unique_features.set (t, hb::unique_ptr<hb_set_t> {hb_set_create ()}))) return; if (unique_features.has (t))
unique_features.get (t)->add (i);
duplicate_feature_map->set (i, i); continue;
}
bool found = false;
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 **p = nullptr; if (feature_substitutes_map->has (i, &p))
f = *p;
#ifndef HB_NO_VAR if (!colr.has_var_store () || !variation_indices) return;
const OT::ItemVariationStore &var_store = colr.get_var_store (); // generated inner_maps is used by ItemVariationStore serialize(), which is subset only unsigned subtable_count = var_store.get_sub_table_count ();
_generate_varstore_inner_maps (variation_indices, subtable_count, plan->colrv1_varstore_inner_maps);
/* colr variation indices mapping during planning phase: * generate colrv1_variation_idx_delta_map. When delta set index map is not * included, it's a mapping from varIdx-> (new varIdx,delta). Otherwise, it's * a mapping from old delta set idx-> (new delta set idx, delta). Mapping * delta set indices is the same as gid mapping. * Besides, we need to generate a delta set idx-> new var_idx map for updating * delta set index map if exists. This map will be updated again after
* instancing. */ if (!plan->all_axes_pinned)
{
_remap_variation_indices (var_store,
variation_indices,
plan->normalized_coords, false, /* no need to calculate delta for COLR during planning */
plan->all_axes_pinned,
plan->colrv1_variation_idx_delta_map);
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 ()); if (!unicode_to_gid) {
_fill_unicode_and_glyph_map(plan, unicodes->iter(), [&] (hb_codepoint_t cp) {
hb_codepoint_t gid; if (!cmap.get_nominal_glyph (cp, &gid)) { return HB_MAP_VALUE_INVALID;
} return 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.
_fill_unicode_and_glyph_map(plan, unicodes->iter(), [&] (hb_codepoint_t cp) { return unicode_to_gid->get (cp);
});
}
} else
{ // This approach is slower, but can handle adding in glyphs to the subset and will match // them with cmap entries.
/* Add gids which where requested, but not mapped in cmap */ unsigned num_glyphs = plan->source->get_num_glyphs ();
hb_codepoint_t first = HB_SET_VALUE_INVALID, last = HB_SET_VALUE_INVALID; for (; glyphs->next_range (&first, &last); )
{ if (first >= num_glyphs) break; if (last >= num_glyphs)
last = num_glyphs - 1;
plan->_glyphset_gsub.add_range (first, last);
}
}
auto &arr = plan->unicode_to_new_gid_list; if (arr.length)
{
plan->unicodes.add_sorted_array (&arr.arrayZ->first, arr.length, sizeof (*arr.arrayZ));
plan->_glyphset_gsub.add_array (&arr.arrayZ->second, arr.length, sizeof (*arr.arrayZ));
}
// Variation selectors don't have glyphs associated with them in the cmap so they will have been filtered out above // but should still be retained. Add them back here.
// However, the min and max codepoints for OS/2 should be calculated without considering variation selectors, // so record those first.
plan->os2_info.min_cmap_codepoint = plan->unicodes.get_min();
plan->os2_info.max_cmap_codepoint = plan->unicodes.get_max();
staticvoid
_populate_gids_to_retain (hb_subset_plan_t* plan,
hb_set_t* drop_tables)
{
OT::glyf_accelerator_t glyf (plan->source); #ifndef HB_NO_SUBSET_CFF // Note: we cannot use inprogress_accelerator here, since it has not been // created yet. So in case of preprocessed-face (and otherwise), we do an // extra sanitize pass here, which is not ideal.
OT::cff1::accelerator_subset_t stack_cff (plan->accelerator ? nullptr : plan->source); const OT::cff1::accelerator_subset_t *cff (plan->accelerator ? plan->accelerator->cff1_accel.get () : &stack_cff); #endif
staticbool
_create_old_gid_to_new_gid_map (const hb_face_t *face, bool retain_gids, const hb_set_t *all_gids_to_retain, const hb_map_t *requested_glyph_map,
hb_map_t *glyph_map, /* OUT */
hb_map_t *reverse_glyph_map, /* OUT */
hb_sorted_vector_t<hb_codepoint_pair_t> *new_to_old_gid_list /* OUT */, unsignedint *num_glyphs /* OUT */)
{ unsigned pop = all_gids_to_retain->get_population ();
reverse_glyph_map->alloc (pop);
glyph_map->alloc (pop);
new_to_old_gid_list->alloc (pop);
if (*requested_glyph_map)
{
hb_set_t new_gids(requested_glyph_map->values()); if (new_gids.get_population() != requested_glyph_map->get_population())
{
DEBUG_MSG (SUBSET, nullptr, "The provided custom glyph mapping is not unique."); returnfalse;
}
if (retain_gids)
{
DEBUG_MSG (SUBSET, nullptr, "HB_SUBSET_FLAGS_RETAIN_GIDS cannot be set if " "a custom glyph mapping has been provided."); returnfalse;
}
// Anything that wasn't mapped by the requested mapping should // be placed after the requested mapping. for (auto old_gid : remaining)
new_to_old_gid_list->push (hb_pair (++max_glyph, old_gid));
int normalized_min = axis.normalize_axis_value (axis_range->minimum); int normalized_default = axis.normalize_axis_value (axis_range->middle); int normalized_max = axis.normalize_axis_value (axis_range->maximum);
if (_hmtx.has_data ())
{ int hori_aw = _hmtx.get_advance_without_var_unscaled (old_gid); if (_hmtx.var_table.get_length ())
hori_aw += (int) roundf (_hmtx.var_table->get_advance_delta_unscaled (old_gid, font->coords, font->num_coords,
hvar_store_cache)); int lsb = extents.x_bearing; if (!has_bounds_info)
{ if (!_hmtx.get_leading_bearing_without_var_unscaled (old_gid, &lsb)) continue;
}
plan->hmtx_map.set (new_gid, hb_pair ((unsigned) hori_aw, lsb));
plan->bounds_width_vec[new_gid] = extents.width;
}
if (_vmtx.has_data ())
{ int vert_aw = _vmtx.get_advance_without_var_unscaled (old_gid); if (_vmtx.var_table.get_length ())
vert_aw += (int) roundf (_vmtx.var_table->get_advance_delta_unscaled (old_gid, font->coords, font->num_coords,
vvar_store_cache));
int tsb = extents.y_bearing; if (!has_bounds_info)
{ if (!_vmtx.get_leading_bearing_without_var_unscaled (old_gid, &tsb)) continue;
}
plan->vmtx_map.set (new_gid, hb_pair ((unsigned) vert_aw, tsb));
plan->bounds_height_vec[new_gid] = extents.height;
}
}
hb_font_destroy (font); if (hvar_store_cache)
_hmtx.var_table->get_var_store ().destroy_cache (hvar_store_cache); if (vvar_store_cache)
_vmtx.var_table->get_var_store ().destroy_cache (vvar_store_cache);
}
staticbool
_get_instance_glyphs_contour_points (hb_subset_plan_t *plan)
{ /* contour_points vector only needed for updating gvar table (infer delta and
* iup delta optimization) during partial instancing */ if (plan->user_axes_location.is_empty () || plan->all_axes_pinned) returntrue;
OT::glyf_accelerator_t glyf (plan->source);
for (auto &_ : plan->new_to_old_gid_list)
{
hb_codepoint_t new_gid = _.first;
contour_point_vector_t all_points; if (new_gid == 0 && !(plan->flags & HB_SUBSET_FLAGS_NOTDEF_OUTLINE))
{ if (unlikely (!plan->new_gid_contour_points_map.set (new_gid, all_points))) returnfalse; continue;
}
hb_codepoint_t old_gid = _.second; auto glyph = glyf.glyph_for_gid (old_gid); if (unlikely (!glyph.get_all_points_without_var (plan->source, all_points))) returnfalse; if (unlikely (!plan->new_gid_contour_points_map.set (new_gid, all_points))) returnfalse;
/* composite new gids are only needed by iup delta optimization */ if ((plan->flags & HB_SUBSET_FLAGS_OPTIMIZE_IUP_DELTAS) && glyph.is_composite ())
plan->composite_new_gids.add (new_gid);
} returntrue;
} #endif
// Now that we have old to new gid map update the unicode to new gid list. for (unsigned i = 0; i < unicode_to_new_gid_list.length; i++)
{ // Use raw array access for performance.
unicode_to_new_gid_list.arrayZ[i].second =
glyph_map->get(unicode_to_new_gid_list.arrayZ[i].second);
}
bounds_width_vec.resize (_num_output_glyphs, false); for (auto &v : bounds_width_vec)
v = 0xFFFFFFFF;
bounds_height_vec.resize (_num_output_glyphs, false); for (auto &v : bounds_height_vec)
v = 0xFFFFFFFF;
if (!drop_tables.has (HB_OT_TAG_GDEF))
_remap_used_mark_sets (this, used_mark_sets_map);
if (inprogress_accelerator)
hb_subset_accelerator_t::destroy ((void*) inprogress_accelerator);
}
/** * hb_subset_plan_create_or_fail: * @face: font face to create the plan for. * @input: a #hb_subset_input_t input. * * Computes a plan for subsetting the supplied face according * to a provided input. The plan describes * which tables and glyphs should be retained. * * Return value: (transfer full): New subset plan. Destroy with * hb_subset_plan_destroy(). If there is a failure creating the plan * nullptr will be returned. * * Since: 4.0.0
**/
hb_subset_plan_t *
hb_subset_plan_create_or_fail (hb_face_t *face, const hb_subset_input_t *input)
{
hb_subset_plan_t *plan; if (unlikely (!(plan = hb_object_create<hb_subset_plan_t> (face, input)))) return nullptr;
if (unlikely (plan->in_error ()))
{
hb_subset_plan_destroy (plan); return nullptr;
}
return plan;
}
/** * hb_subset_plan_destroy: * @plan: a #hb_subset_plan_t * * Decreases the reference count on @plan, and if it reaches zero, destroys * @plan, freeing all memory. * * Since: 4.0.0
**/ void
hb_subset_plan_destroy (hb_subset_plan_t *plan)
{ if (!hb_object_destroy (plan)) return;
hb_free (plan);
}
/** * hb_subset_plan_old_to_new_glyph_mapping: * @plan: a subsetting plan. * * Returns the mapping between glyphs in the original font to glyphs in the * subset that will be produced by @plan * * Return value: (transfer none): * A pointer to the #hb_map_t of the mapping. * * Since: 4.0.0
**/
hb_map_t *
hb_subset_plan_old_to_new_glyph_mapping (const hb_subset_plan_t *plan)
{ return plan->glyph_map;
}
/** * hb_subset_plan_new_to_old_glyph_mapping: * @plan: a subsetting plan. * * Returns the mapping between glyphs in the subset that will be produced by * @plan and the glyph in the original font. * * Return value: (transfer none): * A pointer to the #hb_map_t of the mapping. * * Since: 4.0.0
**/
hb_map_t *
hb_subset_plan_new_to_old_glyph_mapping (const hb_subset_plan_t *plan)
{ return plan->reverse_glyph_map;
}
/** * hb_subset_plan_unicode_to_old_glyph_mapping: * @plan: a subsetting plan. * * Returns the mapping between codepoints in the original font and the * associated glyph id in the original font. * * Return value: (transfer none): * A pointer to the #hb_map_t of the mapping. * * Since: 4.0.0
**/
hb_map_t *
hb_subset_plan_unicode_to_old_glyph_mapping (const hb_subset_plan_t *plan)
{ return plan->codepoint_to_glyph;
}
/** * hb_subset_plan_set_user_data: (skip) * @plan: a #hb_subset_plan_t object. * @key: The user-data key to set * @data: A pointer to the user data * @destroy: (nullable): A callback to call when @data is not needed anymore * @replace: Whether to replace an existing data with the same key * * Attaches a user-data key/data pair to the given subset plan object. * * Return value: `true` if success, `false` otherwise * * Since: 4.0.0
**/
hb_bool_t
hb_subset_plan_set_user_data (hb_subset_plan_t *plan,
hb_user_data_key_t *key, void *data,
hb_destroy_func_t destroy,
hb_bool_t replace)
{ return hb_object_set_user_data (plan, key, data, destroy, replace);
}
/** * hb_subset_plan_get_user_data: (skip) * @plan: a #hb_subset_plan_t object. * @key: The user-data key to query * * Fetches the user data associated with the specified key, * attached to the specified subset plan object. * * Return value: (transfer none): A pointer to the user data * * Since: 4.0.0
**/ void *
hb_subset_plan_get_user_data (const hb_subset_plan_t *plan,
hb_user_data_key_t *key)
{ return hb_object_get_user_data (plan, key);
}
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.