/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 | | |
33 | | using OT::Layout::GSUB; |
34 | | using OT::Layout::GPOS; |
35 | | |
36 | | #ifndef HB_NO_SUBSET_LAYOUT |
37 | | |
38 | | void |
39 | | remap_used_mark_sets (hb_subset_plan_t *plan, |
40 | | hb_map_t& used_mark_sets_map) |
41 | 0 | { |
42 | 0 | hb_blob_ptr_t<OT::GDEF> gdef = plan->source_table<OT::GDEF> (); |
43 | |
|
44 | 0 | if (!gdef->has_data () || !gdef->has_mark_glyph_sets ()) |
45 | 0 | { |
46 | 0 | gdef.destroy (); |
47 | 0 | return; |
48 | 0 | } |
49 | | |
50 | 0 | hb_set_t used_mark_sets; |
51 | 0 | gdef->get_mark_glyph_sets ().collect_used_mark_sets (plan->_glyphset_gsub, used_mark_sets); |
52 | 0 | gdef.destroy (); |
53 | |
|
54 | 0 | remap_indexes (&used_mark_sets, &used_mark_sets_map); |
55 | 0 | } |
56 | | |
57 | | /* |
58 | | * Removes all tags from 'tags' that are not in filter. Additionally eliminates any duplicates. |
59 | | * Returns true if anything was removed (not including duplicates). |
60 | | */ |
61 | | static bool _filter_tag_list(hb_vector_t<hb_tag_t>* tags, /* IN/OUT */ |
62 | | const hb_set_t* filter) |
63 | 0 | { |
64 | 0 | hb_vector_t<hb_tag_t> out; |
65 | 0 | out.alloc (tags->get_size() + 1); // +1 is to allocate room for the null terminator. |
66 | |
|
67 | 0 | bool removed = false; |
68 | 0 | hb_set_t visited; |
69 | |
|
70 | 0 | for (hb_tag_t tag : *tags) |
71 | 0 | { |
72 | 0 | if (!tag) continue; |
73 | 0 | if (visited.has (tag)) continue; |
74 | | |
75 | 0 | if (!filter->has (tag)) |
76 | 0 | { |
77 | 0 | removed = true; |
78 | 0 | continue; |
79 | 0 | } |
80 | | |
81 | 0 | visited.add (tag); |
82 | 0 | out.push (tag); |
83 | 0 | } |
84 | | |
85 | | // The collect function needs a null element to signal end of the array. |
86 | 0 | out.push (HB_TAG_NONE); |
87 | |
|
88 | 0 | hb_swap (out, *tags); |
89 | 0 | return removed; |
90 | 0 | } |
91 | | |
92 | | template <typename T> |
93 | | static void _collect_layout_indices (hb_subset_plan_t *plan, |
94 | | const T& table, |
95 | | hb_set_t *lookup_indices, /* OUT */ |
96 | | hb_set_t *feature_indices, /* OUT */ |
97 | | hb_hashmap_t<unsigned, hb::shared_ptr<hb_set_t>> *feature_record_cond_idx_map, /* OUT */ |
98 | | hb_hashmap_t<unsigned, const OT::Feature*> *feature_substitutes_map, /* OUT */ |
99 | | hb_set_t& catch_all_record_feature_idxes, /* OUT */ |
100 | | hb_hashmap_t<unsigned, hb_pair_t<const void*, const void*>>& catch_all_record_idx_feature_map /* OUT */) |
101 | 0 | { |
102 | 0 | unsigned num_features = table.get_feature_count (); |
103 | 0 | hb_vector_t<hb_tag_t> features; |
104 | 0 | if (!plan->check_success (features.resize (num_features))) return; |
105 | 0 | table.get_feature_tags (0, &num_features, features.arrayZ); |
106 | 0 | bool retain_all_features = !_filter_tag_list (&features, &plan->layout_features); |
107 | |
|
108 | 0 | unsigned num_scripts = table.get_script_count (); |
109 | 0 | hb_vector_t<hb_tag_t> scripts; |
110 | 0 | if (!plan->check_success (scripts.resize (num_scripts))) return; |
111 | 0 | table.get_script_tags (0, &num_scripts, scripts.arrayZ); |
112 | 0 | bool retain_all_scripts = !_filter_tag_list (&scripts, &plan->layout_scripts); |
113 | |
|
114 | 0 | if (!plan->check_success (!features.in_error ()) || !features |
115 | 0 | || !plan->check_success (!scripts.in_error ()) || !scripts) |
116 | 0 | return; |
117 | | |
118 | 0 | hb_ot_layout_collect_features (plan->source, |
119 | 0 | T::tableTag, |
120 | 0 | retain_all_scripts ? nullptr : scripts.arrayZ, |
121 | 0 | nullptr, |
122 | 0 | retain_all_features ? nullptr : features.arrayZ, |
123 | 0 | feature_indices); |
124 | |
|
125 | 0 | #ifndef HB_NO_VAR |
126 | | // collect feature substitutes with variations |
127 | 0 | if (!plan->user_axes_location.is_empty ()) |
128 | 0 | { |
129 | 0 | hb_hashmap_t<hb::shared_ptr<hb_map_t>, unsigned> conditionset_map; |
130 | 0 | OT::hb_collect_feature_substitutes_with_var_context_t c = |
131 | 0 | { |
132 | 0 | &plan->axes_old_index_tag_map, |
133 | 0 | &plan->axes_location, |
134 | 0 | feature_record_cond_idx_map, |
135 | 0 | feature_substitutes_map, |
136 | 0 | catch_all_record_feature_idxes, |
137 | 0 | feature_indices, |
138 | 0 | false, |
139 | 0 | false, |
140 | 0 | false, |
141 | 0 | 0, |
142 | 0 | &conditionset_map |
143 | 0 | }; |
144 | 0 | table.collect_feature_substitutes_with_variations (&c); |
145 | 0 | } |
146 | 0 | #endif |
147 | |
|
148 | 0 | for (unsigned feature_index : *feature_indices) |
149 | 0 | { |
150 | 0 | const OT::Feature* f = &(table.get_feature (feature_index)); |
151 | 0 | const OT::Feature **p = nullptr; |
152 | 0 | if (feature_substitutes_map->has (feature_index, &p)) |
153 | 0 | f = *p; |
154 | |
|
155 | 0 | f->add_lookup_indexes_to (lookup_indices); |
156 | 0 | } |
157 | |
|
158 | 0 | #ifndef HB_NO_VAR |
159 | 0 | if (catch_all_record_feature_idxes) |
160 | 0 | { |
161 | 0 | for (unsigned feature_index : catch_all_record_feature_idxes) |
162 | 0 | { |
163 | 0 | const OT::Feature& f = table.get_feature (feature_index); |
164 | 0 | f.add_lookup_indexes_to (lookup_indices); |
165 | 0 | const void *tag = reinterpret_cast<const void*> (&(table.get_feature_list ().get_tag (feature_index))); |
166 | 0 | catch_all_record_idx_feature_map.set (feature_index, hb_pair (&f, tag)); |
167 | 0 | } |
168 | 0 | } |
169 | | |
170 | | // If all axes are pinned then all feature variations will be dropped so there's no need |
171 | | // to collect lookups from them. |
172 | 0 | if (!plan->all_axes_pinned) |
173 | 0 | table.feature_variation_collect_lookups (feature_indices, |
174 | 0 | plan->user_axes_location.is_empty () ? nullptr: feature_record_cond_idx_map, |
175 | 0 | lookup_indices); |
176 | 0 | #endif |
177 | 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_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_set_t&, hb_hashmap_t<unsigned int, hb_pair_t<void const*, void const*>, false>&) |
178 | | |
179 | | |
180 | | static inline void |
181 | | _GSUBGPOS_find_duplicate_features (const OT::GSUBGPOS &g, |
182 | | const hb_map_t *lookup_indices, |
183 | | const hb_set_t *feature_indices, |
184 | | const hb_hashmap_t<unsigned, const OT::Feature*> *feature_substitutes_map, |
185 | | hb_map_t *duplicate_feature_map /* OUT */) |
186 | 0 | { |
187 | 0 | if (feature_indices->is_empty ()) return; |
188 | 0 | hb_hashmap_t<hb_tag_t, hb::unique_ptr<hb_set_t>> unique_features; |
189 | | //find out duplicate features after subset |
190 | 0 | for (unsigned i : feature_indices->iter ()) |
191 | 0 | { |
192 | 0 | hb_tag_t t = g.get_feature_tag (i); |
193 | 0 | if (t == HB_MAP_VALUE_INVALID) continue; |
194 | 0 | if (!unique_features.has (t)) |
195 | 0 | { |
196 | 0 | if (unlikely (!unique_features.set (t, hb::unique_ptr<hb_set_t> {hb_set_create ()}))) |
197 | 0 | return; |
198 | 0 | if (unique_features.has (t)) |
199 | 0 | unique_features.get (t)->add (i); |
200 | 0 | duplicate_feature_map->set (i, i); |
201 | 0 | continue; |
202 | 0 | } |
203 | | |
204 | 0 | bool found = false; |
205 | |
|
206 | 0 | hb_set_t* same_tag_features = unique_features.get (t); |
207 | 0 | for (unsigned other_f_index : same_tag_features->iter ()) |
208 | 0 | { |
209 | 0 | const OT::Feature* f = &(g.get_feature (i)); |
210 | 0 | const OT::Feature **p = nullptr; |
211 | 0 | if (feature_substitutes_map->has (i, &p)) |
212 | 0 | f = *p; |
213 | |
|
214 | 0 | const OT::Feature* other_f = &(g.get_feature (other_f_index)); |
215 | 0 | if (feature_substitutes_map->has (other_f_index, &p)) |
216 | 0 | other_f = *p; |
217 | |
|
218 | 0 | auto f_iter = |
219 | 0 | + hb_iter (f->lookupIndex) |
220 | 0 | | hb_filter (lookup_indices) |
221 | 0 | ; |
222 | |
|
223 | 0 | auto other_f_iter = |
224 | 0 | + hb_iter (other_f->lookupIndex) |
225 | 0 | | hb_filter (lookup_indices) |
226 | 0 | ; |
227 | |
|
228 | 0 | bool is_equal = true; |
229 | 0 | for (; f_iter && other_f_iter; f_iter++, other_f_iter++) |
230 | 0 | { |
231 | 0 | unsigned a = *f_iter; |
232 | 0 | unsigned b = *other_f_iter; |
233 | 0 | if (a != b) { is_equal = false; break; } |
234 | 0 | } |
235 | |
|
236 | 0 | if (is_equal == false || f_iter || other_f_iter) continue; |
237 | | |
238 | 0 | found = true; |
239 | 0 | duplicate_feature_map->set (i, other_f_index); |
240 | 0 | break; |
241 | 0 | } |
242 | |
|
243 | 0 | if (found == false) |
244 | 0 | { |
245 | 0 | same_tag_features->add (i); |
246 | 0 | duplicate_feature_map->set (i, i); |
247 | 0 | } |
248 | 0 | } |
249 | 0 | } |
250 | | |
251 | | static void |
252 | | remap_feature_indices (const hb_set_t &feature_indices, |
253 | | const hb_map_t &duplicate_feature_map, |
254 | | const hb_hashmap_t<unsigned, hb_pair_t<const void*, const void*>>& catch_all_record_idx_feature_map, |
255 | | hb_map_t *mapping, /* OUT */ |
256 | | hb_map_t *mapping_w_duplicates /* OUT */) |
257 | 0 | { |
258 | 0 | unsigned i = 0; |
259 | 0 | for (const auto _ : feature_indices) |
260 | 0 | { |
261 | | // retain those features in case we need to insert a catch-all record to reinstate the old features |
262 | 0 | if (catch_all_record_idx_feature_map.has (_)) |
263 | 0 | { |
264 | 0 | mapping->set (_, i); |
265 | 0 | mapping_w_duplicates->set (_, i); |
266 | 0 | i++; |
267 | 0 | } |
268 | 0 | else |
269 | 0 | { |
270 | 0 | uint32_t f_idx = duplicate_feature_map.get (_); |
271 | 0 | uint32_t *new_idx; |
272 | 0 | if (mapping-> has (f_idx, &new_idx)) |
273 | 0 | { |
274 | 0 | mapping_w_duplicates->set (_, *new_idx); |
275 | 0 | } |
276 | 0 | else |
277 | 0 | { |
278 | 0 | mapping->set (_, i); |
279 | 0 | mapping_w_duplicates->set (_, i); |
280 | 0 | i++; |
281 | 0 | } |
282 | 0 | } |
283 | 0 | } |
284 | 0 | } |
285 | | |
286 | | template <typename T> |
287 | | static void |
288 | | _closure_glyphs_lookups_features (hb_subset_plan_t *plan, |
289 | | hb_set_t *gids_to_retain, |
290 | | hb_map_t *lookups, |
291 | | hb_map_t *features, |
292 | | hb_map_t *features_w_duplicates, |
293 | | script_langsys_map *langsys_map, |
294 | | hb_hashmap_t<unsigned, hb::shared_ptr<hb_set_t>> *feature_record_cond_idx_map, |
295 | | hb_hashmap_t<unsigned, const OT::Feature*> *feature_substitutes_map, |
296 | | hb_set_t &catch_all_record_feature_idxes, |
297 | | hb_hashmap_t<unsigned, hb_pair_t<const void*, const void*>>& catch_all_record_idx_feature_map) |
298 | 0 | { |
299 | 0 | hb_blob_ptr_t<T> table = plan->source_table<T> (); |
300 | 0 | hb_tag_t table_tag = table->tableTag; |
301 | 0 | hb_set_t lookup_indices, feature_indices; |
302 | 0 | _collect_layout_indices<T> (plan, |
303 | 0 | *table, |
304 | 0 | &lookup_indices, |
305 | 0 | &feature_indices, |
306 | 0 | feature_record_cond_idx_map, |
307 | 0 | feature_substitutes_map, |
308 | 0 | catch_all_record_feature_idxes, |
309 | 0 | catch_all_record_idx_feature_map); |
310 | |
|
311 | 0 | if (table_tag == HB_OT_TAG_GSUB && !(plan->flags & HB_SUBSET_FLAGS_NO_LAYOUT_CLOSURE)) |
312 | 0 | hb_ot_layout_lookups_substitute_closure (plan->source, |
313 | 0 | &lookup_indices, |
314 | 0 | gids_to_retain); |
315 | 0 | table->closure_lookups (plan->source, |
316 | 0 | gids_to_retain, |
317 | 0 | &lookup_indices); |
318 | 0 | remap_indexes (&lookup_indices, lookups); |
319 | | |
320 | | // prune features |
321 | 0 | table->prune_features (lookups, |
322 | 0 | plan->user_axes_location.is_empty () ? nullptr : feature_record_cond_idx_map, |
323 | 0 | feature_substitutes_map, |
324 | 0 | &feature_indices); |
325 | 0 | hb_map_t duplicate_feature_map; |
326 | 0 | _GSUBGPOS_find_duplicate_features (*table, lookups, &feature_indices, feature_substitutes_map, &duplicate_feature_map); |
327 | |
|
328 | 0 | feature_indices.clear (); |
329 | 0 | table->prune_langsys (&duplicate_feature_map, &plan->layout_scripts, langsys_map, &feature_indices); |
330 | 0 | remap_feature_indices (feature_indices, duplicate_feature_map, catch_all_record_idx_feature_map, features, features_w_duplicates); |
331 | |
|
332 | 0 | table.destroy (); |
333 | 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_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_set_t&, hb_hashmap_t<unsigned int, hb_pair_t<void const*, void const*>, false>&) |
334 | | |
335 | | void layout_nameid_closure (hb_subset_plan_t* plan, |
336 | | hb_set_t* drop_tables) |
337 | 6.57k | { |
338 | 6.57k | if (!drop_tables->has (HB_OT_TAG_GPOS)) |
339 | 0 | { |
340 | 0 | hb_blob_ptr_t<GPOS> gpos = plan->source_table<GPOS> (); |
341 | 0 | gpos->collect_name_ids (&plan->gpos_features, &plan->name_ids); |
342 | 0 | gpos.destroy (); |
343 | 0 | } |
344 | 6.57k | if (!drop_tables->has (HB_OT_TAG_GSUB)) |
345 | 0 | { |
346 | 0 | hb_blob_ptr_t<GSUB> gsub = plan->source_table<GSUB> (); |
347 | 0 | gsub->collect_name_ids (&plan->gsub_features, &plan->name_ids); |
348 | 0 | gsub.destroy (); |
349 | 0 | } |
350 | 6.57k | } |
351 | | |
352 | | void |
353 | | layout_populate_gids_to_retain (hb_subset_plan_t* plan, |
354 | 6.57k | hb_set_t* drop_tables) { |
355 | 6.57k | if (!drop_tables->has (HB_OT_TAG_GSUB)) |
356 | | // closure all glyphs/lookups/features needed for GSUB substitutions. |
357 | 0 | _closure_glyphs_lookups_features<GSUB> ( |
358 | 0 | plan, |
359 | 0 | &plan->_glyphset_gsub, |
360 | 0 | &plan->gsub_lookups, |
361 | 0 | &plan->gsub_features, |
362 | 0 | &plan->gsub_features_w_duplicates, |
363 | 0 | &plan->gsub_langsys, |
364 | 0 | &plan->gsub_feature_record_cond_idx_map, |
365 | 0 | &plan->gsub_feature_substitutes_map, |
366 | 0 | plan->gsub_old_features, |
367 | 0 | plan->gsub_old_feature_idx_tag_map); |
368 | | |
369 | 6.57k | if (!drop_tables->has (HB_OT_TAG_GPOS)) |
370 | 0 | _closure_glyphs_lookups_features<GPOS> ( |
371 | 0 | plan, |
372 | 0 | &plan->_glyphset_gsub, |
373 | 0 | &plan->gpos_lookups, |
374 | 0 | &plan->gpos_features, |
375 | 0 | &plan->gpos_features_w_duplicates, |
376 | 0 | &plan->gpos_langsys, |
377 | 0 | &plan->gpos_feature_record_cond_idx_map, |
378 | 0 | &plan->gpos_feature_substitutes_map, |
379 | 0 | plan->gpos_old_features, |
380 | 0 | plan->gpos_old_feature_idx_tag_map); |
381 | 6.57k | } |
382 | | |
383 | | #ifndef HB_NO_VAR |
384 | | void |
385 | | collect_layout_variation_indices (hb_subset_plan_t* plan) |
386 | 0 | { |
387 | 0 | hb_blob_ptr_t<OT::GDEF> gdef = plan->source_table<OT::GDEF> (); |
388 | 0 | hb_blob_ptr_t<GPOS> gpos = plan->source_table<GPOS> (); |
389 | |
|
390 | 0 | if (!gdef->has_data () || !gdef->has_var_store ()) |
391 | 0 | { |
392 | 0 | gdef.destroy (); |
393 | 0 | gpos.destroy (); |
394 | 0 | return; |
395 | 0 | } |
396 | | |
397 | 0 | hb_set_t varidx_set; |
398 | 0 | OT::hb_collect_variation_indices_context_t c (&varidx_set, |
399 | 0 | &plan->_glyphset_gsub, |
400 | 0 | &plan->gpos_lookups); |
401 | 0 | gdef->collect_variation_indices (&c); |
402 | |
|
403 | 0 | if (hb_ot_layout_has_positioning (plan->source)) |
404 | 0 | gpos->collect_variation_indices (&c); |
405 | |
|
406 | 0 | remap_variation_indices (gdef->get_var_store (), |
407 | 0 | varidx_set, plan->normalized_coords, |
408 | 0 | !plan->pinned_at_default, |
409 | 0 | plan->all_axes_pinned, |
410 | 0 | plan->layout_variation_idx_delta_map); |
411 | |
|
412 | 0 | unsigned subtable_count = gdef->get_var_store ().get_sub_table_count (); |
413 | 0 | generate_varstore_inner_maps (varidx_set, subtable_count, plan->gdef_varstore_inner_maps); |
414 | |
|
415 | 0 | gdef.destroy (); |
416 | 0 | gpos.destroy (); |
417 | 0 | } |
418 | | #endif |
419 | | |
420 | | #endif |