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