#ifndef OT_VAR_VARC_VARC_SUBSET_HH #define OT_VAR_VARC_VARC_SUBSET_HH #include "VARC.hh" namespace OT { struct varc_subset_plan_t { varc_subset_plan_t (const VARC &source_, hb_subset_context_t *subset_context_, const Coverage &source_coverage_, const CFF2Index &source_records_, const ConditionList &source_conditions_, const CFF2Index &source_axis_indices_, const MultiItemVariationStore &source_var_store_) : source (source_), subset_context (subset_context_), source_coverage (source_coverage_), source_records (source_records_), source_conditions (source_conditions_), source_axis_indices (source_axis_indices_), source_var_store (source_var_store_) {} bool collect_glyphs () { const hb_set_t &glyphset = subset_context->plan->_glyphset_varced; for (auto _ : + hb_zip (source_coverage, hb_range ((unsigned) source_records.count))) { hb_codepoint_t old_gid = _.first; unsigned index = _.second; if (glyphset.has (old_gid)) { hb_codepoint_t new_gid; if (unlikely (!subset_context->plan->new_gid_for_old_gid (old_gid, &new_gid))) return false; old_indices.push (index); new_gids.push (new_gid); data_size = hb_unsigned_add_saturate (data_size, source_records[index].length); } } return !old_indices.in_error () && !new_gids.in_error () && data_size != UINT_MAX; } bool collect_indices () { for (unsigned old_index : old_indices) { hb_ubytes_t remaining = source_records[old_index]; while (remaining) { VarComponent::record_t component; if (unlikely (!VarComponent::decompile_record (source, remaining, nullptr, nullptr, &component))) return false; if (component.flags & (unsigned) VarComponent::flags_t::HAVE_CONDITION) { if (unlikely (component.condition_index >= source_conditions.get_count ())) return false; condition_indices.add (component.condition_index); } if (component.flags & (unsigned) VarComponent::flags_t::HAVE_AXES) { if (unlikely (component.axis_indices_index >= source_axis_indices.count)) return false; axis_indices.add (component.axis_indices_index); } if ((component.flags & (unsigned) VarComponent::flags_t::AXIS_VALUES_HAVE_VARIATION) && component.axis_values_var_idx != VarIdx::NO_VARIATION) var_indices.add (component.axis_values_var_idx); if ((component.flags & (unsigned) VarComponent::flags_t::TRANSFORM_HAS_VARIATION) && component.transform_var_idx != VarIdx::NO_VARIATION) var_indices.add (component.transform_var_idx); remaining = remaining.sub_array (component.size); } } if (unlikely (condition_indices.in_error () || axis_indices.in_error () || var_indices.in_error ())) return false; for (hb_codepoint_t condition_index : condition_indices) if (unlikely (!source_conditions[condition_index].collect_var_indices (&var_indices))) return false; return true; } bool create_maps () { condition_map.add_set (&condition_indices); axis_indices_map.add_set (&axis_indices); return !condition_map.in_error () && !axis_indices_map.in_error () && source_var_store.create_subset_plan (var_indices, &var_inner_maps, &varidx_map); } bool compile_records () { if (unlikely (!records.alloc_exact (old_indices.length))) return false; bool retain_gids = subset_context->plan->flags & HB_SUBSET_FLAGS_RETAIN_GIDS; if (retain_gids && auxiliary_indices_unchanged ()) { /* The records reference the same indices, so retain them byte-for-byte. */ for (unsigned old_index : old_indices) records.push (source_records[old_index]); } else { hb_vector_t record_offsets; record_data.alloc (data_size); if (unlikely (!record_offsets.alloc_exact (old_indices.length + 1))) return false; for (unsigned old_index : old_indices) { record_offsets.push (record_data.length); if (unlikely (!compile_record (source_records[old_index], retain_gids))) return false; } record_offsets.push (record_data.length); if (unlikely (record_data.in_error () || record_offsets.in_error ())) return false; for (unsigned i = 0; i < old_indices.length; i++) records.push (hb_ubytes_t (record_data.arrayZ + record_offsets[i], record_offsets[i + 1] - record_offsets[i])); data_size = record_data.length; } return !records.in_error (); } bool select_axis_indices () { if (unlikely (!selected_axis_indices.alloc_exact ( axis_indices_map.get_population ()))) return false; for (unsigned i = 0; i < axis_indices_map.get_population (); i++) { hb_ubytes_t bytes = source_axis_indices[axis_indices_map.backward (i)]; axis_indices_data_size = hb_unsigned_add_saturate (axis_indices_data_size, bytes.length); selected_axis_indices.push (bytes); } return axis_indices_data_size != UINT_MAX && !selected_axis_indices.in_error (); } const VARC &source; hb_subset_context_t *subset_context; const Coverage &source_coverage; const CFF2Index &source_records; const ConditionList &source_conditions; const CFF2Index &source_axis_indices; const MultiItemVariationStore &source_var_store; hb_vector_t old_indices; hb_sorted_vector_t new_gids; unsigned data_size = 0; hb_set_t condition_indices; hb_set_t axis_indices; hb_set_t var_indices; hb_inc_bimap_t condition_map; hb_inc_bimap_t axis_indices_map; hb_vector_t var_inner_maps; hb_map_t varidx_map; hb_vector_t record_data; hb_vector_t records; hb_vector_t selected_axis_indices; unsigned axis_indices_data_size = 0; private: bool auxiliary_indices_unchanged () const { for (hb_codepoint_t old_index : condition_indices) if (condition_map.get (old_index) != old_index) return false; for (hb_codepoint_t old_index : axis_indices) if (axis_indices_map.get (old_index) != old_index) return false; for (hb_codepoint_t old_index : var_indices) if (varidx_map.get (old_index) != old_index) return false; return true; } bool append_varint (uint32_t value) { unsigned size = HBUINT32VAR::get_size (value); unsigned offset = record_data.length; unsigned new_length = hb_unsigned_add_saturate (offset, size); if (unlikely (new_length == UINT_MAX || !record_data.resize_dirty (new_length))) return false; HBUINT32VAR::serialize_unsafe (record_data.arrayZ + offset, value); return true; } bool append_bytes (hb_ubytes_t record, unsigned component_size, unsigned start, unsigned end) { if (unlikely (start > end || end > component_size)) return false; unsigned offset = record_data.length; unsigned size = end - start; unsigned new_length = hb_unsigned_add_saturate (offset, size); if (unlikely (new_length == UINT_MAX || !record_data.resize_dirty (new_length))) return false; hb_memcpy (record_data.arrayZ + offset, record.arrayZ + start, size); return true; } bool compile_record (hb_ubytes_t record, bool retain_gids) { hb_ubytes_t remaining = record; while (remaining) { VarComponent::record_t component; if (unlikely (!VarComponent::decompile_record (source, remaining, nullptr, nullptr, &component) || !compile_component (remaining, component, retain_gids))) return false; remaining = remaining.sub_array (component.size); } return true; } bool compile_component (hb_ubytes_t record, const VarComponent::record_t &component, bool retain_gids) { hb_codepoint_t new_gid = component.gid; if (unlikely ((!retain_gids && !subset_context->plan->new_gid_for_old_gid (component.gid, &new_gid)) || new_gid > 0xFFFFFFu)) return false; uint32_t flags = component.flags; unsigned gid_size = component.gid_size; if (new_gid > 0xFFFFu) { flags |= (unsigned) VarComponent::flags_t::GID_IS_24BIT; gid_size = HBGlyphID24::static_size; } if (unlikely (!append_varint (flags))) return false; unsigned gid_offset = record_data.length; unsigned gid_end = hb_unsigned_add_saturate (gid_offset, gid_size); if (unlikely (gid_end == UINT_MAX || !record_data.resize_dirty (gid_end))) return false; unsigned char *out = record_data.arrayZ + gid_offset; if (gid_size == HBGlyphID16::static_size) * (HBGlyphID16 *) out = new_gid; else * (HBGlyphID24 *) out = new_gid; unsigned cursor = component.gid_offset + component.gid_size; if (flags & (unsigned) VarComponent::flags_t::HAVE_CONDITION) { if (unlikely (!condition_map.has (component.condition_index) || !append_bytes (record, component.size, cursor, component.condition_offset) || !append_varint (condition_map.get (component.condition_index)))) return false; cursor = component.condition_offset + component.condition_size; } if (flags & (unsigned) VarComponent::flags_t::HAVE_AXES) { if (unlikely (!axis_indices_map.has (component.axis_indices_index) || !append_bytes (record, component.size, cursor, component.axis_indices_offset) || !append_varint (axis_indices_map.get (component.axis_indices_index)))) return false; cursor = component.axis_indices_offset + component.axis_indices_size; } if (flags & (unsigned) VarComponent::flags_t::AXIS_VALUES_HAVE_VARIATION) { uint32_t var_idx = component.axis_values_var_idx; if (unlikely ((var_idx != VarIdx::NO_VARIATION && !varidx_map.has (var_idx)) || !append_bytes (record, component.size, cursor, component.axis_values_var_offset) || !append_varint (var_idx == VarIdx::NO_VARIATION ? var_idx : varidx_map.get (var_idx)))) return false; cursor = component.axis_values_var_offset + component.axis_values_var_size; } if (flags & (unsigned) VarComponent::flags_t::TRANSFORM_HAS_VARIATION) { uint32_t var_idx = component.transform_var_idx; if (unlikely ((var_idx != VarIdx::NO_VARIATION && !varidx_map.has (var_idx)) || !append_bytes (record, component.size, cursor, component.transform_var_offset) || !append_varint (var_idx == VarIdx::NO_VARIATION ? var_idx : varidx_map.get (var_idx)))) return false; cursor = component.transform_var_offset + component.transform_var_size; } return append_bytes (record, component.size, cursor, component.size); } }; bool VARC::subset (hb_subset_context_t *c) const { TRACE_SUBSET (this); auto fail = [&] () { c->serializer->err (HB_SERIALIZE_ERROR_OTHER); return false; }; /* VARC instancing is not supported yet. In particular, copying the * auxiliary lists while fvar axes are removed would leave stale axis * indices in the table. */ if (unlikely (!c->plan->user_axes_location.is_empty ())) return_trace (fail ()); const Coverage &source_coverage = this+coverage; const CFF2Index &source_records = this+glyphRecords; const ConditionList &source_conditions = this+conditionList; const TupleList &source_axis_indices = this+axisIndicesList; const CFF2Index &source_axis_indices_index = source_axis_indices; const MultiItemVariationStore &source_var_store = this+varStore; varc_subset_plan_t subset_plan (*this, c, source_coverage, source_records, source_conditions, source_axis_indices_index, source_var_store); if (unlikely (!subset_plan.collect_glyphs ())) return_trace (fail ()); if (!subset_plan.old_indices) return_trace (false); if (unlikely (!subset_plan.collect_indices () || !subset_plan.create_maps () || !subset_plan.compile_records ())) return_trace (fail ()); VARC *out = c->serializer->start_embed (); if (unlikely (!out || !c->serializer->extend_min (out))) return_trace (false); out->version = version; if (unlikely (!subset_plan.select_axis_indices ())) return_trace (fail ()); if (unlikely (!out->coverage.serialize_serialize (c->serializer, subset_plan.new_gids.iter ()) || (subset_plan.condition_map.get_population () && !out->conditionList.serialize_serialize (c->serializer, &source_conditions, subset_plan.condition_map, subset_plan.varidx_map)) || (subset_plan.axis_indices_map.get_population () && !out->axisIndicesList.serialize_serialize (c->serializer, subset_plan.selected_axis_indices.iter (), &subset_plan.axis_indices_data_size)) || (subset_plan.var_indices.get_population () && !out->varStore.serialize_serialize (c->serializer, &source_var_store, subset_plan.var_inner_maps.as_array ())) || !out->glyphRecords.serialize_serialize (c->serializer, subset_plan.records.iter (), &subset_plan.data_size))) return_trace (false); return_trace (true); } } /* namespace OT */ #endif /* OT_VAR_VARC_VARC_SUBSET_HH */