/src/harfbuzz/src/hb-subset-plan-layout.cc
Line | Count | Source |
1 | | /* |
2 | | * Copyright © 2023 Google, Inc. |
3 | | * |
4 | | * This is part of HarfBuzz, a text shaping library. |
5 | | * |
6 | | * Permission is hereby granted, without written agreement and without |
7 | | * license or royalty fees, to use, copy, modify, and distribute this |
8 | | * software and its documentation for any purpose, provided that the |
9 | | * above copyright notice and the following two paragraphs appear in |
10 | | * all copies of this software. |
11 | | * |
12 | | * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR |
13 | | * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES |
14 | | * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN |
15 | | * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH |
16 | | * DAMAGE. |
17 | | * |
18 | | * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, |
19 | | * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND |
20 | | * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS |
21 | | * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO |
22 | | * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. |
23 | | * |
24 | | * Google Author(s): Garret Rieger, Qunxin Liu, Roderick Sheeter |
25 | | */ |
26 | | |
27 | | #include "hb-subset-plan.hh" |
28 | | |
29 | | #include "hb-ot-layout-gdef-table.hh" |
30 | | #include "hb-ot-layout-gpos-table.hh" |
31 | | #include "hb-ot-layout-gsub-table.hh" |
32 | | #include "hb-ot-math-table.hh" |
33 | | |
34 | | using OT::Layout::GSUB; |
35 | | using OT::Layout::GPOS; |
36 | | |
37 | | #ifndef HB_NO_SUBSET_LAYOUT |
38 | | |
39 | | double |
40 | | hb_subset_plan_t::layout_variation_delta (unsigned var_idx HB_UNUSED) |
41 | 0 | { |
42 | 0 | #ifndef HB_NO_VAR |
43 | 0 | hb_blob_ptr_t<OT::GDEF> gdef = source_table<OT::GDEF> (); |
44 | 0 | double delta = gdef->get_var_store ().get_delta<double> (var_idx, |
45 | 0 | normalized_coords.as_array ()); |
46 | 0 | return delta; |
47 | | #else |
48 | | return 0.f; |
49 | | #endif |
50 | 0 | } |
51 | | |
52 | | void |
53 | | remap_used_mark_sets (hb_subset_plan_t *plan, |
54 | | hb_map_t& used_mark_sets_map) |
55 | 0 | { |
56 | 0 | hb_blob_ptr_t<OT::GDEF> gdef = plan->source_table<OT::GDEF> (); |
57 | |
|
58 | 0 | if (!gdef->has_data () || !gdef->has_mark_glyph_sets ()) |
59 | 0 | { |
60 | 0 | return; |
61 | 0 | } |
62 | | |
63 | 0 | hb_set_t used_mark_sets; |
64 | 0 | gdef->get_mark_glyph_sets ().collect_used_mark_sets (plan->_glyphset_gsub, used_mark_sets); |
65 | |
|
66 | 0 | remap_indexes (&used_mark_sets, &used_mark_sets_map); |
67 | 0 | } |
68 | | |
69 | | /* |
70 | | * Removes all tags from 'tags' that are not in filter. Additionally eliminates any duplicates. |
71 | | * Returns true if anything was removed (not including duplicates). |
72 | | */ |
73 | | static bool _filter_tag_list(hb_vector_t<hb_tag_t>* tags, /* IN/OUT */ |
74 | | const hb_set_t* filter) |
75 | 0 | { |
76 | 0 | hb_vector_t<hb_tag_t> out; |
77 | 0 | out.alloc (tags->get_size() + 1); // +1 is to allocate room for the null terminator. |
78 | |
|
79 | 0 | bool removed = false; |
80 | 0 | hb_set_t visited; |
81 | |
|
82 | 0 | for (hb_tag_t tag : *tags) |
83 | 0 | { |
84 | 0 | if (!tag) continue; |
85 | 0 | if (visited.has (tag)) continue; |
86 | | |
87 | 0 | if (!filter->has (tag)) |
88 | 0 | { |
89 | 0 | removed = true; |
90 | 0 | continue; |
91 | 0 | } |
92 | | |
93 | 0 | visited.add (tag); |
94 | 0 | out.push (tag); |
95 | 0 | } |
96 | | |
97 | | // The collect function needs a null element to signal end of the array. |
98 | 0 | out.push (HB_TAG_NONE); |
99 | |
|
100 | 0 | hb_swap (out, *tags); |
101 | 0 | return removed; |
102 | 0 | } |
103 | | |
104 | | template <typename T> |
105 | | static bool |
106 | | _collect_instanced_lookup_variations ( |
107 | | hb_subset_plan_t *plan, |
108 | | const T& table, |
109 | | const hb_set_t *feature_indices, |
110 | | hb_hashmap_t<unsigned, hb::shared_ptr<hb_set_t>> *lookup_variations) |
111 | 0 | { |
112 | 0 | hb_blob_ptr_t<OT::GDEF> gdef = plan->source_table<OT::GDEF> (); |
113 | 0 | OT::ItemVarStoreInstancer instancer (&gdef->get_var_store (), nullptr, |
114 | 0 | plan->normalized_coords.as_array ()); |
115 | 0 | hb_vector_t<unsigned> state; |
116 | 0 | unsigned variations_index; |
117 | 0 | bool success = table.get_feature_variations_state ( |
118 | 0 | plan->normalized_coords.arrayZ, |
119 | 0 | plan->normalized_coords.length, |
120 | 0 | &variations_index, |
121 | 0 | &instancer, |
122 | 0 | &state); |
123 | |
|
124 | 0 | if (unlikely (!success || state.in_error ())) |
125 | 0 | return plan->check_success (false); |
126 | | |
127 | 0 | const auto &variations = table.get_feature_variations (); |
128 | 0 | for (const auto &record : variations.get_lookup_variation_records ()) |
129 | 0 | { |
130 | 0 | unsigned feature_index = record.get_feature_index (); |
131 | 0 | if (!feature_indices->has (feature_index)) continue; |
132 | | |
133 | 0 | hb::shared_ptr<hb_set_t> lookups {hb_set_create ()}; |
134 | 0 | record.collect_lookups (&variations, |
135 | 0 | table.get_feature_variation (feature_index, variations_index), |
136 | 0 | state.as_sorted_array (), lookups); |
137 | |
|
138 | 0 | if (unlikely (lookups->in_error () || |
139 | 0 | !lookup_variations->set (feature_index, lookups))) |
140 | 0 | return plan->check_success (false); |
141 | 0 | } |
142 | | |
143 | 0 | return true; |
144 | 0 | } Unexecuted instantiation: hb-subset-plan-layout.cc:bool _collect_instanced_lookup_variations<OT::Layout::GSUB>(hb_subset_plan_t*, OT::Layout::GSUB const&, hb_set_t const*, hb_hashmap_t<unsigned int, hb::shared_ptr<hb_set_t>, false>*) Unexecuted instantiation: hb-subset-plan-layout.cc:bool _collect_instanced_lookup_variations<OT::Layout::GPOS>(hb_subset_plan_t*, OT::Layout::GPOS const&, hb_set_t const*, hb_hashmap_t<unsigned int, hb::shared_ptr<hb_set_t>, false>*) |
145 | | |
146 | | template <typename T> |
147 | | static void _collect_layout_indices (hb_subset_plan_t *plan, |
148 | | const T& table, |
149 | | hb_set_t *lookup_indices, /* OUT */ |
150 | | hb_set_t *feature_indices, /* OUT */ |
151 | | hb_hashmap_t<unsigned, hb::shared_ptr<hb_set_t>> *feature_record_cond_idx_map, /* OUT */ |
152 | | hb_hashmap_t<unsigned, const OT::Feature*> *feature_substitutes_map, /* OUT */ |
153 | | hb_hashmap_t<unsigned, hb::shared_ptr<hb_set_t>> *lookup_variations, /* OUT */ |
154 | | hb_set_t& catch_all_record_feature_idxes, /* OUT */ |
155 | | hb_hashmap_t<unsigned, hb_pair_t<const void*, const void*>>& catch_all_record_idx_feature_map /* OUT */) |
156 | 0 | { |
157 | 0 | unsigned num_features = table.get_feature_count (); |
158 | 0 | hb_vector_t<hb_tag_t> features; |
159 | 0 | if (!plan->check_success (features.resize (num_features))) return; |
160 | 0 | table.get_feature_tags (0, &num_features, features.arrayZ); |
161 | 0 | bool retain_all_features = !_filter_tag_list (&features, &plan->layout_features); |
162 | |
|
163 | 0 | unsigned num_scripts = table.get_script_count (); |
164 | 0 | hb_vector_t<hb_tag_t> scripts; |
165 | 0 | if (!plan->check_success (scripts.resize (num_scripts))) return; |
166 | 0 | table.get_script_tags (0, &num_scripts, scripts.arrayZ); |
167 | 0 | bool retain_all_scripts = !_filter_tag_list (&scripts, &plan->layout_scripts); |
168 | |
|
169 | 0 | if (!plan->check_success (!features.in_error ()) || !features |
170 | 0 | || !plan->check_success (!scripts.in_error ()) || !scripts) |
171 | 0 | return; |
172 | | |
173 | 0 | hb_ot_layout_collect_features (plan->source, |
174 | 0 | T::tableTag, |
175 | 0 | retain_all_scripts ? nullptr : scripts.arrayZ, |
176 | 0 | nullptr, |
177 | 0 | retain_all_features ? nullptr : features.arrayZ, |
178 | 0 | feature_indices); |
179 | |
|
180 | 0 | #ifndef HB_NO_VAR |
181 | | // collect feature substitutes with variations |
182 | 0 | if (!plan->user_axes_location.is_empty ()) |
183 | 0 | { |
184 | 0 | hb_blob_ptr_t<OT::GDEF> gdef = plan->source_table<OT::GDEF> (); |
185 | 0 | OT::ItemVarStoreInstancer instancer (&gdef->get_var_store (), nullptr, |
186 | 0 | plan->normalized_coords.as_array ()); |
187 | 0 | hb_hashmap_t<hb::shared_ptr<hb_map_t>, unsigned> conditionset_map; |
188 | 0 | OT::hb_collect_feature_substitutes_with_var_context_t c = |
189 | 0 | { |
190 | 0 | &plan->axes_old_index_tag_map, |
191 | 0 | &plan->axes_location, |
192 | 0 | feature_record_cond_idx_map, |
193 | 0 | feature_substitutes_map, |
194 | 0 | catch_all_record_feature_idxes, |
195 | 0 | feature_indices, |
196 | 0 | false, |
197 | 0 | false, |
198 | 0 | false, |
199 | 0 | 0, |
200 | 0 | &conditionset_map, |
201 | 0 | &instancer, |
202 | 0 | plan->all_axes_pinned |
203 | 0 | }; |
204 | 0 | table.collect_feature_substitutes_with_variations (&c); |
205 | 0 | } |
206 | 0 | #endif |
207 | |
|
208 | 0 | #ifndef HB_NO_VAR |
209 | 0 | if (plan->all_axes_pinned && |
210 | 0 | !_collect_instanced_lookup_variations (plan, table, feature_indices, lookup_variations)) |
211 | 0 | return; |
212 | 0 | #endif |
213 | | |
214 | 0 | for (unsigned feature_index : *feature_indices) |
215 | 0 | { |
216 | 0 | hb::shared_ptr<hb_set_t> *lookups; |
217 | 0 | if (lookup_variations->has (feature_index, &lookups)) |
218 | 0 | { |
219 | 0 | lookup_indices->union_ (**lookups); |
220 | 0 | continue; |
221 | 0 | } |
222 | | |
223 | 0 | const OT::Feature* f = &(table.get_feature (feature_index)); |
224 | 0 | const OT::Feature **p = nullptr; |
225 | 0 | if (feature_substitutes_map->has (feature_index, &p)) |
226 | 0 | f = *p; |
227 | |
|
228 | 0 | f->add_lookup_indexes_to (lookup_indices); |
229 | 0 | } |
230 | |
|
231 | 0 | #ifndef HB_NO_VAR |
232 | 0 | if (catch_all_record_feature_idxes) |
233 | 0 | { |
234 | 0 | for (unsigned feature_index : catch_all_record_feature_idxes) |
235 | 0 | { |
236 | 0 | const OT::Feature& f = table.get_feature (feature_index); |
237 | 0 | f.add_lookup_indexes_to (lookup_indices); |
238 | 0 | const void *tag = reinterpret_cast<const void*> (&(table.get_feature_list ().get_tag (feature_index))); |
239 | 0 | catch_all_record_idx_feature_map.set (feature_index, hb_pair (&f, tag)); |
240 | 0 | } |
241 | 0 | } |
242 | | |
243 | | // If all axes are pinned then all feature variations will be dropped so there's no need |
244 | | // to collect lookups from them. |
245 | 0 | if (!plan->all_axes_pinned) |
246 | 0 | table.feature_variation_collect_lookups (feature_indices, |
247 | 0 | plan->user_axes_location.is_empty () ? nullptr: feature_record_cond_idx_map, |
248 | 0 | lookup_indices); |
249 | 0 | #endif |
250 | 0 | } Unexecuted instantiation: hb-subset-plan-layout.cc:void _collect_layout_indices<OT::Layout::GSUB>(hb_subset_plan_t*, OT::Layout::GSUB const&, hb_set_t*, hb_set_t*, hb_hashmap_t<unsigned int, hb::shared_ptr<hb_set_t>, false>*, hb_hashmap_t<unsigned int, OT::Feature const*, false>*, hb_hashmap_t<unsigned int, hb::shared_ptr<hb_set_t>, false>*, hb_set_t&, hb_hashmap_t<unsigned int, hb_pair_t<void const*, void const*>, false>&) Unexecuted instantiation: hb-subset-plan-layout.cc:void _collect_layout_indices<OT::Layout::GPOS>(hb_subset_plan_t*, OT::Layout::GPOS const&, hb_set_t*, hb_set_t*, hb_hashmap_t<unsigned int, hb::shared_ptr<hb_set_t>, false>*, hb_hashmap_t<unsigned int, OT::Feature const*, false>*, hb_hashmap_t<unsigned int, hb::shared_ptr<hb_set_t>, false>*, hb_set_t&, hb_hashmap_t<unsigned int, hb_pair_t<void const*, void const*>, false>&) |
251 | | |
252 | | |
253 | | static bool |
254 | | _features_have_equal_params (const OT::Feature &a HB_UNUSED, |
255 | | const OT::Feature &b HB_UNUSED, |
256 | | hb_tag_t tag HB_UNUSED) |
257 | 0 | { |
258 | 0 | #ifndef HB_NO_LAYOUT_FEATURE_PARAMS |
259 | 0 | if (bool (a.featureParams) != bool (b.featureParams)) return false; |
260 | 0 | if (!a.featureParams) return true; |
261 | 0 | const OT::FeatureParams &ap = a.get_feature_params (); |
262 | 0 | const OT::FeatureParams &bp = b.get_feature_params (); |
263 | 0 | if (tag == HB_TAG ('s','i','z','e')) |
264 | 0 | return !hb_memcmp (&ap.get_size_params (tag), &bp.get_size_params (tag), |
265 | 0 | OT::FeatureParamsSize::static_size); |
266 | 0 | if ((tag & 0xFFFF0000u) == HB_TAG ('s','s','\0','\0')) |
267 | 0 | return !hb_memcmp (&ap.get_stylistic_set_params (tag), &bp.get_stylistic_set_params (tag), |
268 | 0 | OT::FeatureParamsStylisticSet::static_size); |
269 | 0 | if ((tag & 0xFFFF0000u) == HB_TAG ('c','v','\0','\0')) |
270 | 0 | { |
271 | 0 | const auto &ac = ap.get_character_variants_params (tag); |
272 | 0 | const auto &bc = bp.get_character_variants_params (tag); |
273 | 0 | return ac.get_size () == bc.get_size () && |
274 | 0 | !hb_memcmp (&ac, &bc, ac.get_size ()); |
275 | 0 | } |
276 | 0 | #endif |
277 | 0 | return true; |
278 | 0 | } |
279 | | |
280 | | static inline void |
281 | | _GSUBGPOS_find_duplicate_features (const OT::GSUBGPOS &g, |
282 | | const hb_map_t *lookup_indices, |
283 | | const hb_set_t *feature_indices, |
284 | | const hb_hashmap_t<unsigned, const OT::Feature*> *feature_substitutes_map, |
285 | | bool all_axes_pinned HB_UNUSED, |
286 | | hb_map_t *duplicate_feature_map /* OUT */) |
287 | 0 | { |
288 | 0 | if (feature_indices->is_empty ()) return; |
289 | 0 | #ifndef HB_NO_VAR |
290 | 0 | hb_set_t variable_features; |
291 | 0 | if (!all_axes_pinned) |
292 | 0 | g.get_feature_variations ().collect_feature_indices (&variable_features); |
293 | | /* Lookup variations are instanced, not dropped, when all axes are pinned. */ |
294 | 0 | g.get_feature_variations ().collect_lookup_variation_feature_indices (&variable_features); |
295 | 0 | if (unlikely (variable_features.in_error ())) return; |
296 | 0 | #endif |
297 | 0 | hb_hashmap_t<hb_tag_t, hb::unique_ptr<hb_set_t>> unique_features; |
298 | | //find out duplicate features after subset |
299 | 0 | for (unsigned i : feature_indices->iter ()) |
300 | 0 | { |
301 | 0 | hb_tag_t t = g.get_feature_tag (i); |
302 | 0 | if (t == HB_MAP_VALUE_INVALID) continue; |
303 | 0 | if (!unique_features.has (t)) |
304 | 0 | { |
305 | 0 | if (unlikely (!unique_features.set (t, hb::unique_ptr<hb_set_t> {hb_set_create ()}))) |
306 | 0 | return; |
307 | 0 | if (unique_features.has (t)) |
308 | 0 | unique_features.get (t)->add (i); |
309 | 0 | duplicate_feature_map->set (i, i); |
310 | 0 | continue; |
311 | 0 | } |
312 | | |
313 | 0 | bool found = false; |
314 | |
|
315 | 0 | hb_set_t* same_tag_features = unique_features.get (t); |
316 | 0 | for (unsigned other_f_index : same_tag_features->iter ()) |
317 | 0 | { |
318 | 0 | #ifndef HB_NO_VAR |
319 | | /* Matching base lookups do not imply matching alternate behavior. */ |
320 | 0 | if (variable_features.has (i) || variable_features.has (other_f_index)) |
321 | 0 | continue; |
322 | 0 | #endif |
323 | | |
324 | 0 | const OT::Feature* f = &(g.get_feature (i)); |
325 | 0 | const OT::Feature **p = nullptr; |
326 | 0 | if (feature_substitutes_map->has (i, &p)) |
327 | 0 | f = *p; |
328 | |
|
329 | 0 | const OT::Feature* other_f = &(g.get_feature (other_f_index)); |
330 | 0 | if (feature_substitutes_map->has (other_f_index, &p)) |
331 | 0 | other_f = *p; |
332 | |
|
333 | 0 | if (!_features_have_equal_params (*f, *other_f, t)) continue; |
334 | | |
335 | 0 | auto f_iter = |
336 | 0 | + hb_iter (f->lookupIndex) |
337 | 0 | | hb_filter (lookup_indices) |
338 | 0 | ; |
339 | |
|
340 | 0 | auto other_f_iter = |
341 | 0 | + hb_iter (other_f->lookupIndex) |
342 | 0 | | hb_filter (lookup_indices) |
343 | 0 | ; |
344 | |
|
345 | 0 | bool is_equal = true; |
346 | 0 | for (; f_iter && other_f_iter; f_iter++, other_f_iter++) |
347 | 0 | { |
348 | 0 | unsigned a = *f_iter; |
349 | 0 | unsigned b = *other_f_iter; |
350 | 0 | if (a != b) { is_equal = false; break; } |
351 | 0 | } |
352 | |
|
353 | 0 | if (is_equal == false || f_iter || other_f_iter) continue; |
354 | | |
355 | 0 | found = true; |
356 | 0 | duplicate_feature_map->set (i, other_f_index); |
357 | 0 | break; |
358 | 0 | } |
359 | |
|
360 | 0 | if (found == false) |
361 | 0 | { |
362 | 0 | same_tag_features->add (i); |
363 | 0 | duplicate_feature_map->set (i, i); |
364 | 0 | } |
365 | 0 | } |
366 | 0 | } |
367 | | |
368 | | static void |
369 | | remap_feature_indices (const hb_set_t &feature_indices, |
370 | | const hb_map_t &duplicate_feature_map, |
371 | | const hb_hashmap_t<unsigned, hb_pair_t<const void*, const void*>>& catch_all_record_idx_feature_map, |
372 | | hb_map_t *mapping, /* OUT */ |
373 | | hb_map_t *mapping_w_duplicates /* OUT */) |
374 | 0 | { |
375 | 0 | unsigned i = 0; |
376 | 0 | for (const auto _ : feature_indices) |
377 | 0 | { |
378 | | // retain those features in case we need to insert a catch-all record to reinstate the old features |
379 | 0 | if (catch_all_record_idx_feature_map.has (_)) |
380 | 0 | { |
381 | 0 | mapping->set (_, i); |
382 | 0 | mapping_w_duplicates->set (_, i); |
383 | 0 | i++; |
384 | 0 | } |
385 | 0 | else |
386 | 0 | { |
387 | 0 | uint32_t f_idx = duplicate_feature_map.get (_); |
388 | 0 | uint32_t *new_idx; |
389 | 0 | if (mapping-> has (f_idx, &new_idx)) |
390 | 0 | { |
391 | 0 | mapping_w_duplicates->set (_, *new_idx); |
392 | 0 | } |
393 | 0 | else |
394 | 0 | { |
395 | 0 | mapping->set (_, i); |
396 | 0 | mapping_w_duplicates->set (_, i); |
397 | 0 | i++; |
398 | 0 | } |
399 | 0 | } |
400 | 0 | } |
401 | 0 | } |
402 | | |
403 | | template <typename T> |
404 | | static void |
405 | | _closure_glyphs_lookups_features (hb_subset_plan_t *plan, |
406 | | hb_set_t *gids_to_retain, |
407 | | hb_map_t *lookups, |
408 | | hb_map_t *features, |
409 | | hb_map_t *features_w_duplicates, |
410 | | script_langsys_map *langsys_map, |
411 | | hb_hashmap_t<unsigned, hb::shared_ptr<hb_set_t>> *feature_record_cond_idx_map, |
412 | | hb_hashmap_t<unsigned, const OT::Feature*> *feature_substitutes_map, |
413 | | hb_hashmap_t<unsigned, hb::shared_ptr<hb_set_t>> *lookup_variations, |
414 | | hb_set_t &catch_all_record_feature_idxes, |
415 | | hb_hashmap_t<unsigned, hb_pair_t<const void*, const void*>>& catch_all_record_idx_feature_map) |
416 | 0 | { |
417 | 0 | hb_blob_ptr_t<T> table = plan->source_table<T> (); |
418 | 0 | hb_tag_t table_tag = table->tableTag; |
419 | 0 | hb_set_t lookup_indices, feature_indices; |
420 | 0 | _collect_layout_indices<T> (plan, |
421 | 0 | *table, |
422 | 0 | &lookup_indices, |
423 | 0 | &feature_indices, |
424 | 0 | feature_record_cond_idx_map, |
425 | 0 | feature_substitutes_map, |
426 | 0 | lookup_variations, |
427 | 0 | catch_all_record_feature_idxes, |
428 | 0 | catch_all_record_idx_feature_map); |
429 | |
|
430 | 0 | if (table_tag == HB_OT_TAG_GSUB && !(plan->flags & HB_SUBSET_FLAGS_NO_LAYOUT_CLOSURE)) |
431 | 0 | hb_ot_layout_lookups_substitute_closure (plan->source, |
432 | 0 | &lookup_indices, |
433 | 0 | gids_to_retain); |
434 | 0 | table->closure_lookups (plan->source, |
435 | 0 | gids_to_retain, |
436 | 0 | &lookup_indices); |
437 | 0 | remap_indexes (&lookup_indices, lookups); |
438 | | |
439 | | // prune features |
440 | 0 | table->prune_features (lookups, |
441 | 0 | plan->user_axes_location.is_empty () ? nullptr : feature_record_cond_idx_map, |
442 | 0 | feature_substitutes_map, |
443 | 0 | &feature_indices); |
444 | 0 | hb_map_t duplicate_feature_map; |
445 | 0 | _GSUBGPOS_find_duplicate_features (*table, lookups, &feature_indices, feature_substitutes_map, |
446 | 0 | plan->all_axes_pinned, &duplicate_feature_map); |
447 | |
|
448 | 0 | feature_indices.clear (); |
449 | 0 | table->prune_langsys (&duplicate_feature_map, &plan->layout_scripts, langsys_map, &feature_indices); |
450 | 0 | remap_feature_indices (feature_indices, duplicate_feature_map, catch_all_record_idx_feature_map, features, features_w_duplicates); |
451 | |
|
452 | 0 | #ifndef HB_NO_VAR |
453 | | // Populate alternate parameter tags after deduplication. Before this point |
454 | | // the map contains only catch-all features and also controls their retention. |
455 | 0 | if (table->get_feature_variations ().record_count ()) |
456 | 0 | for (auto p : *features_w_duplicates) |
457 | 0 | { |
458 | 0 | const OT::Feature& f = table->get_feature (p.first); |
459 | 0 | const void *tag = &(table->get_feature_list ().get_tag (p.first)); |
460 | 0 | catch_all_record_idx_feature_map.set (p.first, hb_pair (&f, tag)); |
461 | 0 | } |
462 | 0 | #endif |
463 | 0 | } Unexecuted instantiation: hb-subset-plan-layout.cc:void _closure_glyphs_lookups_features<OT::Layout::GSUB>(hb_subset_plan_t*, hb_set_t*, hb_map_t*, hb_map_t*, hb_map_t*, hb_hashmap_t<unsigned int, hb::unique_ptr<hb_set_t>, false>*, hb_hashmap_t<unsigned int, hb::shared_ptr<hb_set_t>, false>*, hb_hashmap_t<unsigned int, OT::Feature const*, false>*, hb_hashmap_t<unsigned int, hb::shared_ptr<hb_set_t>, false>*, hb_set_t&, hb_hashmap_t<unsigned int, hb_pair_t<void const*, void const*>, false>&) Unexecuted instantiation: hb-subset-plan-layout.cc:void _closure_glyphs_lookups_features<OT::Layout::GPOS>(hb_subset_plan_t*, hb_set_t*, hb_map_t*, hb_map_t*, hb_map_t*, hb_hashmap_t<unsigned int, hb::unique_ptr<hb_set_t>, false>*, hb_hashmap_t<unsigned int, hb::shared_ptr<hb_set_t>, false>*, hb_hashmap_t<unsigned int, OT::Feature const*, false>*, hb_hashmap_t<unsigned int, hb::shared_ptr<hb_set_t>, false>*, hb_set_t&, hb_hashmap_t<unsigned int, hb_pair_t<void const*, void const*>, false>&) |
464 | | |
465 | | void layout_nameid_closure (hb_subset_plan_t* plan, |
466 | | hb_set_t* drop_tables) |
467 | 0 | { |
468 | 0 | if (!drop_tables->has (HB_OT_TAG_GPOS)) |
469 | 0 | { |
470 | 0 | hb_blob_ptr_t<GPOS> gpos = plan->source_table<GPOS> (); |
471 | 0 | gpos->collect_name_ids (&plan->gpos_features, &plan->name_ids, |
472 | 0 | &plan->gpos_feature_substitutes_map, |
473 | 0 | plan->user_axes_location.is_empty () ? nullptr : &plan->gpos_feature_record_cond_idx_map, |
474 | 0 | &plan->gpos_old_features, |
475 | 0 | plan->all_axes_pinned); |
476 | 0 | } |
477 | 0 | if (!drop_tables->has (HB_OT_TAG_GSUB)) |
478 | 0 | { |
479 | 0 | hb_blob_ptr_t<GSUB> gsub = plan->source_table<GSUB> (); |
480 | 0 | gsub->collect_name_ids (&plan->gsub_features, &plan->name_ids, |
481 | 0 | &plan->gsub_feature_substitutes_map, |
482 | 0 | plan->user_axes_location.is_empty () ? nullptr : &plan->gsub_feature_record_cond_idx_map, |
483 | 0 | &plan->gsub_old_features, |
484 | 0 | plan->all_axes_pinned); |
485 | 0 | } |
486 | 0 | } |
487 | | |
488 | | void |
489 | | layout_populate_gids_to_retain (hb_subset_plan_t* plan, |
490 | 0 | hb_set_t* drop_tables) { |
491 | 0 | if (!drop_tables->has (HB_OT_TAG_GSUB)) |
492 | | // closure all glyphs/lookups/features needed for GSUB substitutions. |
493 | 0 | _closure_glyphs_lookups_features<GSUB> ( |
494 | 0 | plan, |
495 | 0 | &plan->_glyphset_gsub, |
496 | 0 | &plan->gsub_lookups, |
497 | 0 | &plan->gsub_features, |
498 | 0 | &plan->gsub_features_w_duplicates, |
499 | 0 | &plan->gsub_langsys, |
500 | 0 | &plan->gsub_feature_record_cond_idx_map, |
501 | 0 | &plan->gsub_feature_substitutes_map, |
502 | 0 | &plan->gsub_lookup_variations, |
503 | 0 | plan->gsub_old_features, |
504 | 0 | plan->gsub_old_feature_idx_tag_map); |
505 | |
|
506 | 0 | if (!drop_tables->has (HB_OT_TAG_GPOS)) |
507 | 0 | _closure_glyphs_lookups_features<GPOS> ( |
508 | 0 | plan, |
509 | 0 | &plan->_glyphset_gsub, |
510 | 0 | &plan->gpos_lookups, |
511 | 0 | &plan->gpos_features, |
512 | 0 | &plan->gpos_features_w_duplicates, |
513 | 0 | &plan->gpos_langsys, |
514 | 0 | &plan->gpos_feature_record_cond_idx_map, |
515 | 0 | &plan->gpos_feature_substitutes_map, |
516 | 0 | &plan->gpos_lookup_variations, |
517 | 0 | plan->gpos_old_features, |
518 | 0 | plan->gpos_old_feature_idx_tag_map); |
519 | 0 | } |
520 | | |
521 | | #ifndef HB_NO_VAR |
522 | | void |
523 | | collect_layout_variation_indices (hb_subset_plan_t* plan) |
524 | 0 | { |
525 | 0 | hb_blob_ptr_t<OT::GDEF> gdef = plan->source_table<OT::GDEF> (); |
526 | 0 | hb_blob_ptr_t<GSUB> gsub = plan->source_table<GSUB> (); |
527 | 0 | hb_blob_ptr_t<GPOS> gpos = plan->source_table<GPOS> (); |
528 | |
|
529 | 0 | if (!gdef->has_data () || !gdef->has_var_store ()) |
530 | 0 | { |
531 | 0 | return; |
532 | 0 | } |
533 | | |
534 | 0 | hb_set_t varidx_set; |
535 | 0 | OT::hb_collect_variation_indices_context_t c (&varidx_set, |
536 | 0 | &plan->_glyphset_gsub, |
537 | 0 | &plan->gpos_lookups); |
538 | 0 | if (plan->normalized_coords && !plan->all_axes_pinned) |
539 | 0 | c.condition_values = &plan->layout_condition_values; |
540 | 0 | gdef->collect_variation_indices (&c); |
541 | |
|
542 | 0 | if (hb_ot_layout_has_substitution (plan->source)) |
543 | 0 | gsub->collect_feature_variation_indices (&c); |
544 | 0 | if (hb_ot_layout_has_positioning (plan->source)) |
545 | 0 | { |
546 | 0 | gpos->collect_feature_variation_indices (&c); |
547 | 0 | gpos->collect_variation_indices (&c); |
548 | 0 | } |
549 | |
|
550 | 0 | #ifndef HB_NO_MATH |
551 | 0 | if (!plan->drop_tables.has (HB_OT_TAG_MATH)) |
552 | 0 | { |
553 | 0 | hb_blob_ptr_t<OT::MATH> math = plan->source_table<OT::MATH> (); |
554 | 0 | auto *glyph_set = c.glyph_set; |
555 | 0 | c.glyph_set = &plan->_glyphset_mathed; |
556 | 0 | math->collect_variation_indices (&c, &plan->_glyphset_cmaped); |
557 | 0 | c.glyph_set = glyph_set; |
558 | 0 | } |
559 | 0 | #endif |
560 | |
|
561 | 0 | remap_variation_indices (gdef->get_var_store (), |
562 | 0 | varidx_set, plan->normalized_coords, |
563 | 0 | !plan->pinned_at_default, |
564 | 0 | plan->all_axes_pinned, |
565 | 0 | plan->layout_variation_idx_delta_map); |
566 | |
|
567 | 0 | generate_varstore_inner_maps (gdef->get_var_store (), varidx_set, plan->gdef_varstore_inner_maps); |
568 | 0 | plan->check_success (!plan->layout_condition_values.in_error ()); |
569 | 0 | } |
570 | | #endif |
571 | | |
572 | | #endif |