1 /*
2 * Copyright © 2018 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, Roderick Sheeter
25 */
26
27 #include "hb-subset-plan.hh"
28 #include "hb-subset-accelerator.hh"
29 #include "hb-map.hh"
30 #include "hb-multimap.hh"
31 #include "hb-set.hh"
32
33 #include "hb-ot-cmap-table.hh"
34 #include "hb-ot-glyf-table.hh"
35 #include "hb-ot-layout-base-table.hh"
36 #include "hb-ot-layout-gdef-table.hh"
37 #include "hb-ot-layout-gpos-table.hh"
38 #include "hb-ot-layout-gsub-table.hh"
39 #include "hb-ot-cff1-table.hh"
40 #include "hb-ot-cff2-table.hh"
41 #include "OT/Color/COLR/COLR.hh"
42 #include "OT/Color/COLR/colrv1-closure.hh"
43 #include "OT/Color/CPAL/CPAL.hh"
44 #include "hb-ot-var-fvar-table.hh"
45 #include "hb-ot-var-avar-table.hh"
46 #include "hb-ot-stat-table.hh"
47 #include "hb-ot-math-table.hh"
48
49 using OT::Layout::GSUB;
50 using OT::Layout::GPOS;
51
52
~hb_subset_accelerator_t()53 hb_subset_accelerator_t::~hb_subset_accelerator_t ()
54 {
55 if (cmap_cache && destroy_cmap_cache)
56 destroy_cmap_cache ((void*) cmap_cache);
57
58 #ifndef HB_NO_SUBSET_CFF
59 cff1_accel.fini ();
60 cff2_accel.fini ();
61 #endif
62 hb_face_destroy (source);
63 }
64
65
66 typedef hb_hashmap_t<unsigned, hb::unique_ptr<hb_set_t>> script_langsys_map;
67 #ifndef HB_NO_SUBSET_CFF
68 static inline bool
_add_cff_seac_components(const OT::cff1::accelerator_subset_t & cff,hb_codepoint_t gid,hb_set_t * gids_to_retain)69 _add_cff_seac_components (const OT::cff1::accelerator_subset_t &cff,
70 hb_codepoint_t gid,
71 hb_set_t *gids_to_retain)
72 {
73 hb_codepoint_t base_gid, accent_gid;
74 if (cff.get_seac_components (gid, &base_gid, &accent_gid))
75 {
76 gids_to_retain->add (base_gid);
77 gids_to_retain->add (accent_gid);
78 return true;
79 }
80 return false;
81 }
82 #endif
83
84 static void
_remap_palette_indexes(const hb_set_t * palette_indexes,hb_map_t * mapping)85 _remap_palette_indexes (const hb_set_t *palette_indexes,
86 hb_map_t *mapping /* OUT */)
87 {
88 unsigned new_idx = 0;
89 for (unsigned palette_index : palette_indexes->iter ())
90 {
91 if (palette_index == 0xFFFF)
92 {
93 mapping->set (palette_index, palette_index);
94 continue;
95 }
96 mapping->set (palette_index, new_idx);
97 new_idx++;
98 }
99 }
100
101 static void
_remap_indexes(const hb_set_t * indexes,hb_map_t * mapping)102 _remap_indexes (const hb_set_t *indexes,
103 hb_map_t *mapping /* OUT */)
104 {
105 for (auto _ : + hb_enumerate (indexes->iter ()))
106 mapping->set (_.second, _.first);
107
108 }
109
110 #ifndef HB_NO_SUBSET_LAYOUT
111
112 /*
113 * Removes all tags from 'tags' that are not in filter. Additionally eliminates any duplicates.
114 * Returns true if anything was removed (not including duplicates).
115 */
_filter_tag_list(hb_vector_t<hb_tag_t> * tags,const hb_set_t * filter)116 static bool _filter_tag_list(hb_vector_t<hb_tag_t>* tags, /* IN/OUT */
117 const hb_set_t* filter)
118 {
119 hb_vector_t<hb_tag_t> out;
120 out.alloc (tags->get_size() + 1); // +1 is to allocate room for the null terminator.
121
122 bool removed = false;
123 hb_set_t visited;
124
125 for (hb_tag_t tag : *tags)
126 {
127 if (!tag) continue;
128 if (visited.has (tag)) continue;
129
130 if (!filter->has (tag))
131 {
132 removed = true;
133 continue;
134 }
135
136 visited.add (tag);
137 out.push (tag);
138 }
139
140 // The collect function needs a null element to signal end of the array.
141 out.push (HB_TAG_NONE);
142
143 hb_swap (out, *tags);
144 return removed;
145 }
146
147 template <typename T>
_collect_layout_indices(hb_subset_plan_t * plan,const T & table,hb_set_t * lookup_indices,hb_set_t * feature_indices,hb_hashmap_t<unsigned,hb::shared_ptr<hb_set_t>> * feature_record_cond_idx_map,hb_hashmap_t<unsigned,const OT::Feature * > * feature_substitutes_map,hb_set_t & catch_all_record_feature_idxes,hb_hashmap_t<unsigned,hb_pair_t<const void *,const void * >> & catch_all_record_idx_feature_map)148 static void _collect_layout_indices (hb_subset_plan_t *plan,
149 const T& table,
150 hb_set_t *lookup_indices, /* OUT */
151 hb_set_t *feature_indices, /* OUT */
152 hb_hashmap_t<unsigned, hb::shared_ptr<hb_set_t>> *feature_record_cond_idx_map, /* OUT */
153 hb_hashmap_t<unsigned, const OT::Feature*> *feature_substitutes_map, /* 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 {
157 unsigned num_features = table.get_feature_count ();
158 hb_vector_t<hb_tag_t> features;
159 if (!plan->check_success (features.resize (num_features))) return;
160 table.get_feature_tags (0, &num_features, features.arrayZ);
161 bool retain_all_features = !_filter_tag_list (&features, &plan->layout_features);
162
163 unsigned num_scripts = table.get_script_count ();
164 hb_vector_t<hb_tag_t> scripts;
165 if (!plan->check_success (scripts.resize (num_scripts))) return;
166 table.get_script_tags (0, &num_scripts, scripts.arrayZ);
167 bool retain_all_scripts = !_filter_tag_list (&scripts, &plan->layout_scripts);
168
169 if (!plan->check_success (!features.in_error ()) || !features
170 || !plan->check_success (!scripts.in_error ()) || !scripts)
171 return;
172
173 hb_ot_layout_collect_features (plan->source,
174 T::tableTag,
175 retain_all_scripts ? nullptr : scripts.arrayZ,
176 nullptr,
177 retain_all_features ? nullptr : features.arrayZ,
178 feature_indices);
179
180 #ifndef HB_NO_VAR
181 // collect feature substitutes with variations
182 if (!plan->user_axes_location.is_empty ())
183 {
184 hb_hashmap_t<hb::shared_ptr<hb_map_t>, unsigned> conditionset_map;
185 OT::hb_collect_feature_substitutes_with_var_context_t c =
186 {
187 &plan->axes_old_index_tag_map,
188 &plan->axes_location,
189 feature_record_cond_idx_map,
190 feature_substitutes_map,
191 catch_all_record_feature_idxes,
192 feature_indices,
193 false,
194 false,
195 false,
196 0,
197 &conditionset_map
198 };
199 table.collect_feature_substitutes_with_variations (&c);
200 }
201 #endif
202
203 for (unsigned feature_index : *feature_indices)
204 {
205 const OT::Feature* f = &(table.get_feature (feature_index));
206 const OT::Feature **p = nullptr;
207 if (feature_substitutes_map->has (feature_index, &p))
208 f = *p;
209
210 f->add_lookup_indexes_to (lookup_indices);
211 }
212
213 #ifndef HB_NO_VAR
214 if (catch_all_record_feature_idxes)
215 {
216 for (unsigned feature_index : catch_all_record_feature_idxes)
217 {
218 const OT::Feature& f = table.get_feature (feature_index);
219 f.add_lookup_indexes_to (lookup_indices);
220 const void *tag = reinterpret_cast<const void*> (&(table.get_feature_list ().get_tag (feature_index)));
221 catch_all_record_idx_feature_map.set (feature_index, hb_pair (&f, tag));
222 }
223 }
224
225 // If all axes are pinned then all feature variations will be dropped so there's no need
226 // to collect lookups from them.
227 if (!plan->all_axes_pinned)
228 table.feature_variation_collect_lookups (feature_indices,
229 plan->user_axes_location.is_empty () ? nullptr: feature_record_cond_idx_map,
230 lookup_indices);
231 #endif
232 }
233
234
235 static inline void
_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)236 _GSUBGPOS_find_duplicate_features (const OT::GSUBGPOS &g,
237 const hb_map_t *lookup_indices,
238 const hb_set_t *feature_indices,
239 const hb_hashmap_t<unsigned, const OT::Feature*> *feature_substitutes_map,
240 hb_map_t *duplicate_feature_map /* OUT */)
241 {
242 if (feature_indices->is_empty ()) return;
243 hb_hashmap_t<hb_tag_t, hb::unique_ptr<hb_set_t>> unique_features;
244 //find out duplicate features after subset
245 for (unsigned i : feature_indices->iter ())
246 {
247 hb_tag_t t = g.get_feature_tag (i);
248 if (t == HB_MAP_VALUE_INVALID) continue;
249 if (!unique_features.has (t))
250 {
251 if (unlikely (!unique_features.set (t, hb::unique_ptr<hb_set_t> {hb_set_create ()})))
252 return;
253 if (unique_features.has (t))
254 unique_features.get (t)->add (i);
255 duplicate_feature_map->set (i, i);
256 continue;
257 }
258
259 bool found = false;
260
261 hb_set_t* same_tag_features = unique_features.get (t);
262 for (unsigned other_f_index : same_tag_features->iter ())
263 {
264 const OT::Feature* f = &(g.get_feature (i));
265 const OT::Feature **p = nullptr;
266 if (feature_substitutes_map->has (i, &p))
267 f = *p;
268
269 const OT::Feature* other_f = &(g.get_feature (other_f_index));
270 if (feature_substitutes_map->has (other_f_index, &p))
271 other_f = *p;
272
273 auto f_iter =
274 + hb_iter (f->lookupIndex)
275 | hb_filter (lookup_indices)
276 ;
277
278 auto other_f_iter =
279 + hb_iter (other_f->lookupIndex)
280 | hb_filter (lookup_indices)
281 ;
282
283 bool is_equal = true;
284 for (; f_iter && other_f_iter; f_iter++, other_f_iter++)
285 {
286 unsigned a = *f_iter;
287 unsigned b = *other_f_iter;
288 if (a != b) { is_equal = false; break; }
289 }
290
291 if (is_equal == false || f_iter || other_f_iter) continue;
292
293 found = true;
294 duplicate_feature_map->set (i, other_f_index);
295 break;
296 }
297
298 if (found == false)
299 {
300 same_tag_features->add (i);
301 duplicate_feature_map->set (i, i);
302 }
303 }
304 }
305
306 template <typename T>
307 static inline void
_closure_glyphs_lookups_features(hb_subset_plan_t * plan,hb_set_t * gids_to_retain,hb_map_t * lookups,hb_map_t * features,script_langsys_map * langsys_map,hb_hashmap_t<unsigned,hb::shared_ptr<hb_set_t>> * feature_record_cond_idx_map,hb_hashmap_t<unsigned,const OT::Feature * > * feature_substitutes_map,hb_set_t & catch_all_record_feature_idxes,hb_hashmap_t<unsigned,hb_pair_t<const void *,const void * >> & catch_all_record_idx_feature_map)308 _closure_glyphs_lookups_features (hb_subset_plan_t *plan,
309 hb_set_t *gids_to_retain,
310 hb_map_t *lookups,
311 hb_map_t *features,
312 script_langsys_map *langsys_map,
313 hb_hashmap_t<unsigned, hb::shared_ptr<hb_set_t>> *feature_record_cond_idx_map,
314 hb_hashmap_t<unsigned, const OT::Feature*> *feature_substitutes_map,
315 hb_set_t &catch_all_record_feature_idxes,
316 hb_hashmap_t<unsigned, hb_pair_t<const void*, const void*>>& catch_all_record_idx_feature_map)
317 {
318 hb_blob_ptr_t<T> table = plan->source_table<T> ();
319 hb_tag_t table_tag = table->tableTag;
320 hb_set_t lookup_indices, feature_indices;
321 _collect_layout_indices<T> (plan,
322 *table,
323 &lookup_indices,
324 &feature_indices,
325 feature_record_cond_idx_map,
326 feature_substitutes_map,
327 catch_all_record_feature_idxes,
328 catch_all_record_idx_feature_map);
329
330 if (table_tag == HB_OT_TAG_GSUB && !(plan->flags & HB_SUBSET_FLAGS_NO_LAYOUT_CLOSURE))
331 hb_ot_layout_lookups_substitute_closure (plan->source,
332 &lookup_indices,
333 gids_to_retain);
334 table->closure_lookups (plan->source,
335 gids_to_retain,
336 &lookup_indices);
337 _remap_indexes (&lookup_indices, lookups);
338
339 // prune features
340 table->prune_features (lookups,
341 plan->user_axes_location.is_empty () ? nullptr : feature_record_cond_idx_map,
342 feature_substitutes_map,
343 &feature_indices);
344 hb_map_t duplicate_feature_map;
345 _GSUBGPOS_find_duplicate_features (*table, lookups, &feature_indices, feature_substitutes_map, &duplicate_feature_map);
346
347 feature_indices.clear ();
348 table->prune_langsys (&duplicate_feature_map, &plan->layout_scripts, langsys_map, &feature_indices);
349 _remap_indexes (&feature_indices, features);
350
351 table.destroy ();
352 }
353
354 #endif
355
356 #ifndef HB_NO_VAR
357 static inline void
_generate_varstore_inner_maps(const hb_set_t & varidx_set,unsigned subtable_count,hb_vector_t<hb_inc_bimap_t> & inner_maps)358 _generate_varstore_inner_maps (const hb_set_t& varidx_set,
359 unsigned subtable_count,
360 hb_vector_t<hb_inc_bimap_t> &inner_maps /* OUT */)
361 {
362 if (varidx_set.is_empty () || subtable_count == 0) return;
363
364 if (unlikely (!inner_maps.resize (subtable_count))) return;
365 for (unsigned idx : varidx_set)
366 {
367 uint16_t major = idx >> 16;
368 uint16_t minor = idx & 0xFFFF;
369
370 if (major >= subtable_count)
371 continue;
372 inner_maps[major].add (minor);
373 }
374 }
375
376 static inline hb_font_t*
_get_hb_font_with_variations(const hb_subset_plan_t * plan)377 _get_hb_font_with_variations (const hb_subset_plan_t *plan)
378 {
379 hb_font_t *font = hb_font_create (plan->source);
380
381 hb_vector_t<hb_variation_t> vars;
382 if (!vars.alloc (plan->user_axes_location.get_population ())) {
383 hb_font_destroy (font);
384 return nullptr;
385 }
386
387 for (auto _ : plan->user_axes_location)
388 {
389 hb_variation_t var;
390 var.tag = _.first;
391 var.value = _.second.middle;
392 vars.push (var);
393 }
394
395 #ifndef HB_NO_VAR
396 hb_font_set_variations (font, vars.arrayZ, plan->user_axes_location.get_population ());
397 #endif
398 return font;
399 }
400
401 static inline void
_remap_variation_indices(const OT::ItemVariationStore & var_store,const hb_set_t & variation_indices,const hb_vector_t<int> & normalized_coords,bool calculate_delta,bool no_variations,hb_hashmap_t<unsigned,hb_pair_t<unsigned,int>> & variation_idx_delta_map)402 _remap_variation_indices (const OT::ItemVariationStore &var_store,
403 const hb_set_t &variation_indices,
404 const hb_vector_t<int>& normalized_coords,
405 bool calculate_delta, /* not pinned at default */
406 bool no_variations, /* all axes pinned */
407 hb_hashmap_t<unsigned, hb_pair_t<unsigned, int>> &variation_idx_delta_map /* OUT */)
408 {
409 if (&var_store == &Null (OT::ItemVariationStore)) return;
410 unsigned subtable_count = var_store.get_sub_table_count ();
411 float *store_cache = var_store.create_cache ();
412
413 unsigned new_major = 0, new_minor = 0;
414 unsigned last_major = (variation_indices.get_min ()) >> 16;
415 for (unsigned idx : variation_indices)
416 {
417 int delta = 0;
418 if (calculate_delta)
419 delta = roundf (var_store.get_delta (idx, normalized_coords.arrayZ,
420 normalized_coords.length, store_cache));
421
422 if (no_variations)
423 {
424 variation_idx_delta_map.set (idx, hb_pair_t<unsigned, int> (HB_OT_LAYOUT_NO_VARIATIONS_INDEX, delta));
425 continue;
426 }
427
428 uint16_t major = idx >> 16;
429 if (major >= subtable_count) break;
430 if (major != last_major)
431 {
432 new_minor = 0;
433 ++new_major;
434 }
435
436 unsigned new_idx = (new_major << 16) + new_minor;
437 variation_idx_delta_map.set (idx, hb_pair_t<unsigned, int> (new_idx, delta));
438 ++new_minor;
439 last_major = major;
440 }
441 var_store.destroy_cache (store_cache);
442 }
443
444 static inline void
_collect_layout_variation_indices(hb_subset_plan_t * plan)445 _collect_layout_variation_indices (hb_subset_plan_t* plan)
446 {
447 hb_blob_ptr_t<OT::GDEF> gdef = plan->source_table<OT::GDEF> ();
448 hb_blob_ptr_t<GPOS> gpos = plan->source_table<GPOS> ();
449
450 if (!gdef->has_data () || !gdef->has_var_store ())
451 {
452 gdef.destroy ();
453 gpos.destroy ();
454 return;
455 }
456
457 hb_set_t varidx_set;
458 OT::hb_collect_variation_indices_context_t c (&varidx_set,
459 &plan->_glyphset_gsub,
460 &plan->gpos_lookups);
461 gdef->collect_variation_indices (&c);
462
463 if (hb_ot_layout_has_positioning (plan->source))
464 gpos->collect_variation_indices (&c);
465
466 _remap_variation_indices (gdef->get_var_store (),
467 varidx_set, plan->normalized_coords,
468 !plan->pinned_at_default,
469 plan->all_axes_pinned,
470 plan->layout_variation_idx_delta_map);
471
472 unsigned subtable_count = gdef->get_var_store ().get_sub_table_count ();
473 _generate_varstore_inner_maps (varidx_set, subtable_count, plan->gdef_varstore_inner_maps);
474
475 gdef.destroy ();
476 gpos.destroy ();
477 }
478
479 #ifndef HB_NO_BASE
480 static inline void
_collect_base_variation_indices(hb_subset_plan_t * plan)481 _collect_base_variation_indices (hb_subset_plan_t* plan)
482 {
483 hb_blob_ptr_t<OT::BASE> base = plan->source_table<OT::BASE> ();
484 if (!base->has_var_store ())
485 {
486 base.destroy ();
487 return;
488 }
489
490 hb_set_t varidx_set;
491 base->collect_variation_indices (plan, varidx_set);
492 const OT::ItemVariationStore &var_store = base->get_var_store ();
493 unsigned subtable_count = var_store.get_sub_table_count ();
494
495
496 _remap_variation_indices (var_store, varidx_set,
497 plan->normalized_coords,
498 !plan->pinned_at_default,
499 plan->all_axes_pinned,
500 plan->base_variation_idx_map);
501 _generate_varstore_inner_maps (varidx_set, subtable_count, plan->base_varstore_inner_maps);
502
503 base.destroy ();
504 }
505
506 #endif
507 #endif
508
509 static inline void
_cmap_closure(hb_face_t * face,const hb_set_t * unicodes,hb_set_t * glyphset)510 _cmap_closure (hb_face_t *face,
511 const hb_set_t *unicodes,
512 hb_set_t *glyphset)
513 {
514 OT::cmap::accelerator_t cmap (face);
515 cmap.table->closure_glyphs (unicodes, glyphset);
516 }
517
518 #ifndef HB_NO_VAR
519 static void
_remap_colrv1_delta_set_index_indices(const OT::DeltaSetIndexMap & index_map,const hb_set_t & delta_set_idxes,hb_hashmap_t<unsigned,hb_pair_t<unsigned,int>> & variation_idx_delta_map,hb_map_t & new_deltaset_idx_varidx_map)520 _remap_colrv1_delta_set_index_indices (const OT::DeltaSetIndexMap &index_map,
521 const hb_set_t &delta_set_idxes,
522 hb_hashmap_t<unsigned, hb_pair_t<unsigned, int>> &variation_idx_delta_map, /* IN/OUT */
523 hb_map_t &new_deltaset_idx_varidx_map /* OUT */)
524 {
525 if (!index_map.get_map_count ())
526 return;
527
528 hb_hashmap_t<unsigned, hb_pair_t<unsigned, int>> delta_set_idx_delta_map;
529 unsigned new_delta_set_idx = 0;
530 for (unsigned delta_set_idx : delta_set_idxes)
531 {
532 unsigned var_idx = index_map.map (delta_set_idx);
533 unsigned new_varidx = HB_OT_LAYOUT_NO_VARIATIONS_INDEX;
534 int delta = 0;
535
536 if (var_idx != HB_OT_LAYOUT_NO_VARIATIONS_INDEX)
537 {
538 hb_pair_t<unsigned, int> *new_varidx_delta;
539 if (!variation_idx_delta_map.has (var_idx, &new_varidx_delta)) continue;
540
541 new_varidx = hb_first (*new_varidx_delta);
542 delta = hb_second (*new_varidx_delta);
543 }
544
545 new_deltaset_idx_varidx_map.set (new_delta_set_idx, new_varidx);
546 delta_set_idx_delta_map.set (delta_set_idx, hb_pair_t<unsigned, int> (new_delta_set_idx, delta));
547 new_delta_set_idx++;
548 }
549 variation_idx_delta_map = std::move (delta_set_idx_delta_map);
550 }
551 #endif
552
_colr_closure(hb_subset_plan_t * plan,hb_set_t * glyphs_colred)553 static void _colr_closure (hb_subset_plan_t* plan,
554 hb_set_t *glyphs_colred)
555 {
556 OT::COLR::accelerator_t colr (plan->source);
557 if (!colr.is_valid ()) return;
558
559 hb_set_t palette_indices, layer_indices;
560 // Collect all glyphs referenced by COLRv0
561 hb_set_t glyphset_colrv0;
562 for (hb_codepoint_t gid : *glyphs_colred)
563 colr.closure_glyphs (gid, &glyphset_colrv0);
564
565 glyphs_colred->union_ (glyphset_colrv0);
566
567 //closure for COLRv1
568 hb_set_t variation_indices, delta_set_indices;
569 colr.closure_forV1 (glyphs_colred, &layer_indices, &palette_indices, &variation_indices, &delta_set_indices);
570
571 colr.closure_V0palette_indices (glyphs_colred, &palette_indices);
572 _remap_indexes (&layer_indices, &plan->colrv1_layers);
573 _remap_palette_indexes (&palette_indices, &plan->colr_palettes);
574
575 #ifndef HB_NO_VAR
576 if (!colr.has_var_store () || !variation_indices) return;
577
578 const OT::ItemVariationStore &var_store = colr.get_var_store ();
579 // generated inner_maps is used by ItemVariationStore serialize(), which is subset only
580 unsigned subtable_count = var_store.get_sub_table_count ();
581 _generate_varstore_inner_maps (variation_indices, subtable_count, plan->colrv1_varstore_inner_maps);
582
583 /* colr variation indices mapping during planning phase:
584 * generate colrv1_variation_idx_delta_map. When delta set index map is not
585 * included, it's a mapping from varIdx-> (new varIdx,delta). Otherwise, it's
586 * a mapping from old delta set idx-> (new delta set idx, delta). Mapping
587 * delta set indices is the same as gid mapping.
588 * Besides, we need to generate a delta set idx-> new var_idx map for updating
589 * delta set index map if exists. This map will be updated again after
590 * instancing. */
591 if (!plan->all_axes_pinned)
592 {
593 _remap_variation_indices (var_store,
594 variation_indices,
595 plan->normalized_coords,
596 false, /* no need to calculate delta for COLR during planning */
597 plan->all_axes_pinned,
598 plan->colrv1_variation_idx_delta_map);
599
600 if (colr.has_delta_set_index_map ())
601 _remap_colrv1_delta_set_index_indices (colr.get_delta_set_index_map (),
602 delta_set_indices,
603 plan->colrv1_variation_idx_delta_map,
604 plan->colrv1_new_deltaset_idx_varidx_map);
605 }
606 #endif
607 }
608
609 static inline void
_math_closure(hb_subset_plan_t * plan,hb_set_t * glyphset)610 _math_closure (hb_subset_plan_t *plan,
611 hb_set_t *glyphset)
612 {
613 hb_blob_ptr_t<OT::MATH> math = plan->source_table<OT::MATH> ();
614 if (math->has_data ())
615 math->closure_glyphs (glyphset);
616 math.destroy ();
617 }
618
619 static inline void
_remap_used_mark_sets(hb_subset_plan_t * plan,hb_map_t & used_mark_sets_map)620 _remap_used_mark_sets (hb_subset_plan_t *plan,
621 hb_map_t& used_mark_sets_map)
622 {
623 hb_blob_ptr_t<OT::GDEF> gdef = plan->source_table<OT::GDEF> ();
624
625 if (!gdef->has_data () || !gdef->has_mark_glyph_sets ())
626 {
627 gdef.destroy ();
628 return;
629 }
630
631 hb_set_t used_mark_sets;
632 gdef->get_mark_glyph_sets ().collect_used_mark_sets (plan->_glyphset_gsub, used_mark_sets);
633 gdef.destroy ();
634
635 _remap_indexes (&used_mark_sets, &used_mark_sets_map);
636 }
637
638 static inline void
_remove_invalid_gids(hb_set_t * glyphs,unsigned int num_glyphs)639 _remove_invalid_gids (hb_set_t *glyphs,
640 unsigned int num_glyphs)
641 {
642 glyphs->del_range (num_glyphs, HB_SET_VALUE_INVALID);
643 }
644
645 template<bool GID_ALWAYS_EXISTS = false, typename I, typename F, typename G, hb_requires (hb_is_iterator (I))>
646 static void
_fill_unicode_and_glyph_map(hb_subset_plan_t * plan,I unicode_iterator,F unicode_to_gid_for_iterator,G unicode_to_gid_general)647 _fill_unicode_and_glyph_map(hb_subset_plan_t *plan,
648 I unicode_iterator,
649 F unicode_to_gid_for_iterator,
650 G unicode_to_gid_general)
651 {
652 for (hb_codepoint_t cp : unicode_iterator)
653 {
654 hb_codepoint_t gid = unicode_to_gid_for_iterator(cp);
655 if (!GID_ALWAYS_EXISTS && gid == HB_MAP_VALUE_INVALID)
656 {
657 DEBUG_MSG(SUBSET, nullptr, "Drop U+%04X; no gid", cp);
658 continue;
659 }
660
661 plan->codepoint_to_glyph->set (cp, gid);
662 plan->unicode_to_new_gid_list.push (hb_pair (cp, gid));
663 }
664 }
665
666 template<bool GID_ALWAYS_EXISTS = false, typename I, typename F, hb_requires (hb_is_iterator (I))>
667 static void
_fill_unicode_and_glyph_map(hb_subset_plan_t * plan,I unicode_iterator,F unicode_to_gid_for_iterator)668 _fill_unicode_and_glyph_map(hb_subset_plan_t *plan,
669 I unicode_iterator,
670 F unicode_to_gid_for_iterator)
671 {
672 _fill_unicode_and_glyph_map(plan, unicode_iterator, unicode_to_gid_for_iterator, unicode_to_gid_for_iterator);
673 }
674
675 static void
_populate_unicodes_to_retain(const hb_set_t * unicodes,const hb_set_t * glyphs,hb_subset_plan_t * plan)676 _populate_unicodes_to_retain (const hb_set_t *unicodes,
677 const hb_set_t *glyphs,
678 hb_subset_plan_t *plan)
679 {
680 OT::cmap::accelerator_t cmap (plan->source);
681 unsigned size_threshold = plan->source->get_num_glyphs ();
682
683 if (glyphs->is_empty () && unicodes->get_population () < size_threshold)
684 {
685
686 const hb_map_t* unicode_to_gid = nullptr;
687 if (plan->accelerator)
688 unicode_to_gid = &plan->accelerator->unicode_to_gid;
689
690 // This is approach to collection is faster, but can only be used if glyphs
691 // are not being explicitly added to the subset and the input unicodes set is
692 // not excessively large (eg. an inverted set).
693 plan->unicode_to_new_gid_list.alloc (unicodes->get_population ());
694 if (!unicode_to_gid) {
695 _fill_unicode_and_glyph_map(plan, unicodes->iter(), [&] (hb_codepoint_t cp) {
696 hb_codepoint_t gid;
697 if (!cmap.get_nominal_glyph (cp, &gid)) {
698 return HB_MAP_VALUE_INVALID;
699 }
700 return gid;
701 });
702 } else {
703 // Use in memory unicode to gid map it's faster then looking up from
704 // the map. This code is mostly duplicated from above to avoid doing
705 // conditionals on the presence of the unicode_to_gid map each
706 // iteration.
707 _fill_unicode_and_glyph_map(plan, unicodes->iter(), [&] (hb_codepoint_t cp) {
708 return unicode_to_gid->get (cp);
709 });
710 }
711 }
712 else
713 {
714 // This approach is slower, but can handle adding in glyphs to the subset and will match
715 // them with cmap entries.
716
717 hb_map_t unicode_glyphid_map_storage;
718 hb_set_t cmap_unicodes_storage;
719 const hb_map_t* unicode_glyphid_map = &unicode_glyphid_map_storage;
720 const hb_set_t* cmap_unicodes = &cmap_unicodes_storage;
721
722 if (!plan->accelerator) {
723 cmap.collect_mapping (&cmap_unicodes_storage, &unicode_glyphid_map_storage);
724 plan->unicode_to_new_gid_list.alloc (hb_min(unicodes->get_population ()
725 + glyphs->get_population (),
726 cmap_unicodes->get_population ()));
727 } else {
728 unicode_glyphid_map = &plan->accelerator->unicode_to_gid;
729 cmap_unicodes = &plan->accelerator->unicodes;
730 }
731
732 if (plan->accelerator &&
733 unicodes->get_population () < cmap_unicodes->get_population () &&
734 glyphs->get_population () < cmap_unicodes->get_population ())
735 {
736 plan->codepoint_to_glyph->alloc (unicodes->get_population () + glyphs->get_population ());
737
738 auto &gid_to_unicodes = plan->accelerator->gid_to_unicodes;
739
740 for (hb_codepoint_t gid : *glyphs)
741 {
742 auto unicodes = gid_to_unicodes.get (gid);
743 _fill_unicode_and_glyph_map<true>(plan, unicodes, [&] (hb_codepoint_t cp) {
744 return gid;
745 },
746 [&] (hb_codepoint_t cp) {
747 return unicode_glyphid_map->get(cp);
748 });
749 }
750
751 _fill_unicode_and_glyph_map(plan, unicodes->iter(), [&] (hb_codepoint_t cp) {
752 /* Don't double-add entry. */
753 if (plan->codepoint_to_glyph->has (cp))
754 return HB_MAP_VALUE_INVALID;
755
756 return unicode_glyphid_map->get(cp);
757 },
758 [&] (hb_codepoint_t cp) {
759 return unicode_glyphid_map->get(cp);
760 });
761
762 plan->unicode_to_new_gid_list.qsort ();
763 }
764 else
765 {
766 plan->codepoint_to_glyph->alloc (cmap_unicodes->get_population ());
767 hb_codepoint_t first = HB_SET_VALUE_INVALID, last = HB_SET_VALUE_INVALID;
768 for (; cmap_unicodes->next_range (&first, &last); )
769 {
770 _fill_unicode_and_glyph_map(plan, hb_range(first, last + 1), [&] (hb_codepoint_t cp) {
771 hb_codepoint_t gid = (*unicode_glyphid_map)[cp];
772 if (!unicodes->has (cp) && !glyphs->has (gid))
773 return HB_MAP_VALUE_INVALID;
774 return gid;
775 },
776 [&] (hb_codepoint_t cp) {
777 return unicode_glyphid_map->get(cp);
778 });
779 }
780 }
781
782 /* Add gids which where requested, but not mapped in cmap */
783 unsigned num_glyphs = plan->source->get_num_glyphs ();
784 hb_codepoint_t first = HB_SET_VALUE_INVALID, last = HB_SET_VALUE_INVALID;
785 for (; glyphs->next_range (&first, &last); )
786 {
787 if (first >= num_glyphs)
788 break;
789 if (last >= num_glyphs)
790 last = num_glyphs - 1;
791 plan->_glyphset_gsub.add_range (first, last);
792 }
793 }
794
795 auto &arr = plan->unicode_to_new_gid_list;
796 if (arr.length)
797 {
798 plan->unicodes.add_sorted_array (&arr.arrayZ->first, arr.length, sizeof (*arr.arrayZ));
799 plan->_glyphset_gsub.add_array (&arr.arrayZ->second, arr.length, sizeof (*arr.arrayZ));
800 }
801
802 // Variation selectors don't have glyphs associated with them in the cmap so they will have been filtered out above
803 // but should still be retained. Add them back here.
804
805 // However, the min and max codepoints for OS/2 should be calculated without considering variation selectors,
806 // so record those first.
807 plan->os2_info.min_cmap_codepoint = plan->unicodes.get_min();
808 plan->os2_info.max_cmap_codepoint = plan->unicodes.get_max();
809
810 hb_set_t variation_selectors_to_retain;
811 cmap.collect_variation_selectors(&variation_selectors_to_retain);
812 + variation_selectors_to_retain.iter()
813 | hb_filter(unicodes)
814 | hb_sink(&plan->unicodes)
815 ;
816 }
817
818 static unsigned
_glyf_add_gid_and_children(const OT::glyf_accelerator_t & glyf,hb_codepoint_t gid,hb_set_t * gids_to_retain,int operation_count,unsigned depth=0)819 _glyf_add_gid_and_children (const OT::glyf_accelerator_t &glyf,
820 hb_codepoint_t gid,
821 hb_set_t *gids_to_retain,
822 int operation_count,
823 unsigned depth = 0)
824 {
825 /* Check if is already visited */
826 if (gids_to_retain->has (gid)) return operation_count;
827
828 gids_to_retain->add (gid);
829
830 if (unlikely (depth++ > HB_MAX_NESTING_LEVEL)) return operation_count;
831 if (unlikely (--operation_count < 0)) return operation_count;
832
833 auto glyph = glyf.glyph_for_gid (gid);
834
835 for (auto &item : glyph.get_composite_iterator ())
836 operation_count =
837 _glyf_add_gid_and_children (glyf,
838 item.get_gid (),
839 gids_to_retain,
840 operation_count,
841 depth);
842
843 return operation_count;
844 }
845
846 static void
_nameid_closure(hb_subset_plan_t * plan,hb_set_t * drop_tables)847 _nameid_closure (hb_subset_plan_t* plan,
848 hb_set_t* drop_tables)
849 {
850 #ifndef HB_NO_STYLE
851 plan->source->table.STAT->collect_name_ids (&plan->user_axes_location, &plan->name_ids);
852 #endif
853 #ifndef HB_NO_VAR
854 if (!plan->all_axes_pinned)
855 plan->source->table.fvar->collect_name_ids (&plan->user_axes_location, &plan->axes_old_index_tag_map, &plan->name_ids);
856 #endif
857 #ifndef HB_NO_COLOR
858 if (!drop_tables->has (HB_OT_TAG_CPAL))
859 plan->source->table.CPAL->collect_name_ids (&plan->colr_palettes, &plan->name_ids);
860 #endif
861
862 #ifndef HB_NO_SUBSET_LAYOUT
863 if (!drop_tables->has (HB_OT_TAG_GPOS))
864 {
865 hb_blob_ptr_t<GPOS> gpos = plan->source_table<GPOS> ();
866 gpos->collect_name_ids (&plan->gpos_features, &plan->name_ids);
867 gpos.destroy ();
868 }
869 if (!drop_tables->has (HB_OT_TAG_GSUB))
870 {
871 hb_blob_ptr_t<GSUB> gsub = plan->source_table<GSUB> ();
872 gsub->collect_name_ids (&plan->gsub_features, &plan->name_ids);
873 gsub.destroy ();
874 }
875 #endif
876 }
877
878 static void
_populate_gids_to_retain(hb_subset_plan_t * plan,hb_set_t * drop_tables)879 _populate_gids_to_retain (hb_subset_plan_t* plan,
880 hb_set_t* drop_tables)
881 {
882 OT::glyf_accelerator_t glyf (plan->source);
883 #ifndef HB_NO_SUBSET_CFF
884 // Note: we cannot use inprogress_accelerator here, since it has not been
885 // created yet. So in case of preprocessed-face (and otherwise), we do an
886 // extra sanitize pass here, which is not ideal.
887 OT::cff1::accelerator_subset_t stack_cff (plan->accelerator ? nullptr : plan->source);
888 const OT::cff1::accelerator_subset_t *cff (plan->accelerator ? plan->accelerator->cff1_accel.get () : &stack_cff);
889 #endif
890
891 plan->_glyphset_gsub.add (0); // Not-def
892
893 _cmap_closure (plan->source, &plan->unicodes, &plan->_glyphset_gsub);
894
895 #ifndef HB_NO_SUBSET_LAYOUT
896 if (!drop_tables->has (HB_OT_TAG_GSUB))
897 // closure all glyphs/lookups/features needed for GSUB substitutions.
898 _closure_glyphs_lookups_features<GSUB> (
899 plan,
900 &plan->_glyphset_gsub,
901 &plan->gsub_lookups,
902 &plan->gsub_features,
903 &plan->gsub_langsys,
904 &plan->gsub_feature_record_cond_idx_map,
905 &plan->gsub_feature_substitutes_map,
906 plan->gsub_old_features,
907 plan->gsub_old_feature_idx_tag_map);
908
909 if (!drop_tables->has (HB_OT_TAG_GPOS))
910 _closure_glyphs_lookups_features<GPOS> (
911 plan,
912 &plan->_glyphset_gsub,
913 &plan->gpos_lookups,
914 &plan->gpos_features,
915 &plan->gpos_langsys,
916 &plan->gpos_feature_record_cond_idx_map,
917 &plan->gpos_feature_substitutes_map,
918 plan->gpos_old_features,
919 plan->gpos_old_feature_idx_tag_map);
920 #endif
921 _remove_invalid_gids (&plan->_glyphset_gsub, plan->source->get_num_glyphs ());
922
923 plan->_glyphset_mathed = plan->_glyphset_gsub;
924 if (!drop_tables->has (HB_OT_TAG_MATH))
925 {
926 _math_closure (plan, &plan->_glyphset_mathed);
927 _remove_invalid_gids (&plan->_glyphset_mathed, plan->source->get_num_glyphs ());
928 }
929
930 hb_set_t cur_glyphset = plan->_glyphset_mathed;
931 if (!drop_tables->has (HB_OT_TAG_COLR))
932 {
933 _colr_closure (plan, &cur_glyphset);
934 _remove_invalid_gids (&cur_glyphset, plan->source->get_num_glyphs ());
935 }
936
937 plan->_glyphset_colred = cur_glyphset;
938
939 // XXX TODO VARC closure / subset
940
941 _nameid_closure (plan, drop_tables);
942 /* Populate a full set of glyphs to retain by adding all referenced
943 * composite glyphs. */
944 if (glyf.has_data ())
945 for (hb_codepoint_t gid : cur_glyphset)
946 _glyf_add_gid_and_children (glyf, gid, &plan->_glyphset,
947 cur_glyphset.get_population () * HB_MAX_COMPOSITE_OPERATIONS_PER_GLYPH);
948 else
949 plan->_glyphset.union_ (cur_glyphset);
950 #ifndef HB_NO_SUBSET_CFF
951 if (!plan->accelerator || plan->accelerator->has_seac)
952 {
953 bool has_seac = false;
954 if (cff->is_valid ())
955 for (hb_codepoint_t gid : cur_glyphset)
956 if (_add_cff_seac_components (*cff, gid, &plan->_glyphset))
957 has_seac = true;
958 plan->has_seac = has_seac;
959 }
960 #endif
961
962 _remove_invalid_gids (&plan->_glyphset, plan->source->get_num_glyphs ());
963
964 #ifndef HB_NO_VAR
965 if (!drop_tables->has (HB_OT_TAG_GDEF))
966 _collect_layout_variation_indices (plan);
967 #endif
968 }
969
970 static void
_create_glyph_map_gsub(const hb_set_t * glyph_set_gsub,const hb_map_t * glyph_map,hb_map_t * out)971 _create_glyph_map_gsub (const hb_set_t* glyph_set_gsub,
972 const hb_map_t* glyph_map,
973 hb_map_t* out)
974 {
975 out->alloc (glyph_set_gsub->get_population ());
976 + hb_iter (glyph_set_gsub)
977 | hb_map ([&] (hb_codepoint_t gid) {
978 return hb_codepoint_pair_t (gid, glyph_map->get (gid));
979 })
980 | hb_sink (out)
981 ;
982 }
983
984 static bool
_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,hb_map_t * reverse_glyph_map,hb_sorted_vector_t<hb_codepoint_pair_t> * new_to_old_gid_list,unsigned int * num_glyphs)985 _create_old_gid_to_new_gid_map (const hb_face_t *face,
986 bool retain_gids,
987 const hb_set_t *all_gids_to_retain,
988 const hb_map_t *requested_glyph_map,
989 hb_map_t *glyph_map, /* OUT */
990 hb_map_t *reverse_glyph_map, /* OUT */
991 hb_sorted_vector_t<hb_codepoint_pair_t> *new_to_old_gid_list /* OUT */,
992 unsigned int *num_glyphs /* OUT */)
993 {
994 unsigned pop = all_gids_to_retain->get_population ();
995 reverse_glyph_map->alloc (pop);
996 glyph_map->alloc (pop);
997 new_to_old_gid_list->alloc (pop);
998
999 if (*requested_glyph_map)
1000 {
1001 hb_set_t new_gids(requested_glyph_map->values());
1002 if (new_gids.get_population() != requested_glyph_map->get_population())
1003 {
1004 DEBUG_MSG (SUBSET, nullptr, "The provided custom glyph mapping is not unique.");
1005 return false;
1006 }
1007
1008 if (retain_gids)
1009 {
1010 DEBUG_MSG (SUBSET, nullptr,
1011 "HB_SUBSET_FLAGS_RETAIN_GIDS cannot be set if "
1012 "a custom glyph mapping has been provided.");
1013 return false;
1014 }
1015
1016 hb_codepoint_t max_glyph = 0;
1017 hb_set_t remaining;
1018 for (auto old_gid : all_gids_to_retain->iter ())
1019 {
1020 if (old_gid == 0) {
1021 new_to_old_gid_list->push (hb_pair<hb_codepoint_t, hb_codepoint_t> (0u, 0u));
1022 continue;
1023 }
1024
1025 hb_codepoint_t* new_gid;
1026 if (!requested_glyph_map->has (old_gid, &new_gid))
1027 {
1028 remaining.add(old_gid);
1029 continue;
1030 }
1031
1032 if (*new_gid > max_glyph)
1033 max_glyph = *new_gid;
1034 new_to_old_gid_list->push (hb_pair (*new_gid, old_gid));
1035 }
1036 new_to_old_gid_list->qsort ();
1037
1038 // Anything that wasn't mapped by the requested mapping should
1039 // be placed after the requested mapping.
1040 for (auto old_gid : remaining)
1041 new_to_old_gid_list->push (hb_pair (++max_glyph, old_gid));
1042
1043 *num_glyphs = max_glyph + 1;
1044 }
1045 else if (!retain_gids)
1046 {
1047 + hb_enumerate (hb_iter (all_gids_to_retain), (hb_codepoint_t) 0)
1048 | hb_sink (new_to_old_gid_list)
1049 ;
1050 *num_glyphs = new_to_old_gid_list->length;
1051 }
1052 else
1053 {
1054 + hb_iter (all_gids_to_retain)
1055 | hb_map ([] (hb_codepoint_t _) {
1056 return hb_codepoint_pair_t (_, _);
1057 })
1058 | hb_sink (new_to_old_gid_list)
1059 ;
1060
1061 hb_codepoint_t max_glyph = HB_SET_VALUE_INVALID;
1062 hb_set_previous (all_gids_to_retain, &max_glyph);
1063
1064 *num_glyphs = max_glyph + 1;
1065 }
1066
1067 reverse_glyph_map->alloc (reverse_glyph_map->get_population () + new_to_old_gid_list->length);
1068 + hb_iter (new_to_old_gid_list)
1069 | hb_sink (reverse_glyph_map)
1070 ;
1071 glyph_map->alloc (glyph_map->get_population () + new_to_old_gid_list->length);
1072 + hb_iter (new_to_old_gid_list)
1073 | hb_map (&hb_codepoint_pair_t::reverse)
1074 | hb_sink (glyph_map)
1075 ;
1076
1077 return true;
1078 }
1079
1080 #ifndef HB_NO_VAR
1081 static void
_normalize_axes_location(hb_face_t * face,hb_subset_plan_t * plan)1082 _normalize_axes_location (hb_face_t *face, hb_subset_plan_t *plan)
1083 {
1084 if (plan->user_axes_location.is_empty ())
1085 return;
1086
1087 hb_array_t<const OT::AxisRecord> axes = face->table.fvar->get_axes ();
1088 plan->normalized_coords.resize (axes.length);
1089
1090 bool has_avar = face->table.avar->has_data ();
1091 const OT::SegmentMaps *seg_maps = nullptr;
1092 unsigned avar_axis_count = 0;
1093 if (has_avar)
1094 {
1095 seg_maps = face->table.avar->get_segment_maps ();
1096 avar_axis_count = face->table.avar->get_axis_count();
1097 }
1098
1099 bool axis_not_pinned = false;
1100 unsigned old_axis_idx = 0, new_axis_idx = 0;
1101 for (const auto& axis : axes)
1102 {
1103 hb_tag_t axis_tag = axis.get_axis_tag ();
1104 plan->axes_old_index_tag_map.set (old_axis_idx, axis_tag);
1105
1106 if (!plan->user_axes_location.has (axis_tag) ||
1107 !plan->user_axes_location.get (axis_tag).is_point ())
1108 {
1109 axis_not_pinned = true;
1110 plan->axes_index_map.set (old_axis_idx, new_axis_idx);
1111 plan->axis_tags.push (axis_tag);
1112 new_axis_idx++;
1113 }
1114
1115 Triple *axis_range;
1116 if (plan->user_axes_location.has (axis_tag, &axis_range))
1117 {
1118 plan->axes_triple_distances.set (axis_tag, axis.get_triple_distances ());
1119
1120 int normalized_min = axis.normalize_axis_value (axis_range->minimum);
1121 int normalized_default = axis.normalize_axis_value (axis_range->middle);
1122 int normalized_max = axis.normalize_axis_value (axis_range->maximum);
1123
1124 if (has_avar && old_axis_idx < avar_axis_count)
1125 {
1126 normalized_min = seg_maps->map (normalized_min);
1127 normalized_default = seg_maps->map (normalized_default);
1128 normalized_max = seg_maps->map (normalized_max);
1129 }
1130 plan->axes_location.set (axis_tag, Triple (static_cast<double> (normalized_min / 16384.0),
1131 static_cast<double> (normalized_default / 16384.0),
1132 static_cast<double> (normalized_max / 16384.0)));
1133
1134 if (normalized_default != 0)
1135 plan->pinned_at_default = false;
1136
1137 plan->normalized_coords[old_axis_idx] = normalized_default;
1138 }
1139
1140 old_axis_idx++;
1141
1142 if (has_avar && old_axis_idx < avar_axis_count)
1143 seg_maps = &StructAfter<OT::SegmentMaps> (*seg_maps);
1144 }
1145 plan->all_axes_pinned = !axis_not_pinned;
1146 }
1147
1148 static void
_update_instance_metrics_map_from_cff2(hb_subset_plan_t * plan)1149 _update_instance_metrics_map_from_cff2 (hb_subset_plan_t *plan)
1150 {
1151 if (!plan->normalized_coords) return;
1152 OT::cff2::accelerator_t cff2 (plan->source);
1153 if (!cff2.is_valid ()) return;
1154
1155 hb_font_t *font = _get_hb_font_with_variations (plan);
1156 if (unlikely (!plan->check_success (font != nullptr)))
1157 {
1158 hb_font_destroy (font);
1159 return;
1160 }
1161
1162 hb_glyph_extents_t extents = {0x7FFF, -0x7FFF};
1163 OT::hmtx_accelerator_t _hmtx (plan->source);
1164 float *hvar_store_cache = nullptr;
1165 if (_hmtx.has_data () && _hmtx.var_table.get_length ())
1166 hvar_store_cache = _hmtx.var_table->get_var_store ().create_cache ();
1167
1168 OT::vmtx_accelerator_t _vmtx (plan->source);
1169 float *vvar_store_cache = nullptr;
1170 if (_vmtx.has_data () && _vmtx.var_table.get_length ())
1171 vvar_store_cache = _vmtx.var_table->get_var_store ().create_cache ();
1172
1173 for (auto p : *plan->glyph_map)
1174 {
1175 hb_codepoint_t old_gid = p.first;
1176 hb_codepoint_t new_gid = p.second;
1177 if (!cff2.get_extents (font, old_gid, &extents)) continue;
1178 bool has_bounds_info = true;
1179 if (extents.x_bearing == 0 && extents.width == 0 &&
1180 extents.height == 0 && extents.y_bearing == 0)
1181 has_bounds_info = false;
1182
1183 if (has_bounds_info)
1184 {
1185 plan->head_maxp_info.xMin = hb_min (plan->head_maxp_info.xMin, extents.x_bearing);
1186 plan->head_maxp_info.xMax = hb_max (plan->head_maxp_info.xMax, extents.x_bearing + extents.width);
1187 plan->head_maxp_info.yMax = hb_max (plan->head_maxp_info.yMax, extents.y_bearing);
1188 plan->head_maxp_info.yMin = hb_min (plan->head_maxp_info.yMin, extents.y_bearing + extents.height);
1189 }
1190
1191 if (_hmtx.has_data ())
1192 {
1193 int hori_aw = _hmtx.get_advance_without_var_unscaled (old_gid);
1194 if (_hmtx.var_table.get_length ())
1195 hori_aw += (int) roundf (_hmtx.var_table->get_advance_delta_unscaled (old_gid, font->coords, font->num_coords,
1196 hvar_store_cache));
1197 int lsb = extents.x_bearing;
1198 if (!has_bounds_info)
1199 {
1200 if (!_hmtx.get_leading_bearing_without_var_unscaled (old_gid, &lsb))
1201 continue;
1202 }
1203 plan->hmtx_map.set (new_gid, hb_pair ((unsigned) hori_aw, lsb));
1204 plan->bounds_width_vec[new_gid] = extents.width;
1205 }
1206
1207 if (_vmtx.has_data ())
1208 {
1209 int vert_aw = _vmtx.get_advance_without_var_unscaled (old_gid);
1210 if (_vmtx.var_table.get_length ())
1211 vert_aw += (int) roundf (_vmtx.var_table->get_advance_delta_unscaled (old_gid, font->coords, font->num_coords,
1212 vvar_store_cache));
1213
1214 int tsb = extents.y_bearing;
1215 if (!has_bounds_info)
1216 {
1217 if (!_vmtx.get_leading_bearing_without_var_unscaled (old_gid, &tsb))
1218 continue;
1219 }
1220 plan->vmtx_map.set (new_gid, hb_pair ((unsigned) vert_aw, tsb));
1221 plan->bounds_height_vec[new_gid] = extents.height;
1222 }
1223 }
1224 hb_font_destroy (font);
1225 if (hvar_store_cache)
1226 _hmtx.var_table->get_var_store ().destroy_cache (hvar_store_cache);
1227 if (vvar_store_cache)
1228 _vmtx.var_table->get_var_store ().destroy_cache (vvar_store_cache);
1229 }
1230
1231 static bool
_get_instance_glyphs_contour_points(hb_subset_plan_t * plan)1232 _get_instance_glyphs_contour_points (hb_subset_plan_t *plan)
1233 {
1234 /* contour_points vector only needed for updating gvar table (infer delta and
1235 * iup delta optimization) during partial instancing */
1236 if (plan->user_axes_location.is_empty () || plan->all_axes_pinned)
1237 return true;
1238
1239 OT::glyf_accelerator_t glyf (plan->source);
1240
1241 for (auto &_ : plan->new_to_old_gid_list)
1242 {
1243 hb_codepoint_t new_gid = _.first;
1244 contour_point_vector_t all_points;
1245 if (new_gid == 0 && !(plan->flags & HB_SUBSET_FLAGS_NOTDEF_OUTLINE))
1246 {
1247 if (unlikely (!plan->new_gid_contour_points_map.set (new_gid, all_points)))
1248 return false;
1249 continue;
1250 }
1251
1252 hb_codepoint_t old_gid = _.second;
1253 auto glyph = glyf.glyph_for_gid (old_gid);
1254 if (unlikely (!glyph.get_all_points_without_var (plan->source, all_points)))
1255 return false;
1256 if (unlikely (!plan->new_gid_contour_points_map.set (new_gid, all_points)))
1257 return false;
1258
1259 /* composite new gids are only needed by iup delta optimization */
1260 if ((plan->flags & HB_SUBSET_FLAGS_OPTIMIZE_IUP_DELTAS) && glyph.is_composite ())
1261 plan->composite_new_gids.add (new_gid);
1262 }
1263 return true;
1264 }
1265 #endif
1266
hb_subset_plan_t(hb_face_t * face,const hb_subset_input_t * input)1267 hb_subset_plan_t::hb_subset_plan_t (hb_face_t *face,
1268 const hb_subset_input_t *input)
1269 {
1270 successful = true;
1271 flags = input->flags;
1272
1273 unicode_to_new_gid_list.init ();
1274
1275 name_ids = *input->sets.name_ids;
1276 name_languages = *input->sets.name_languages;
1277 layout_features = *input->sets.layout_features;
1278 layout_scripts = *input->sets.layout_scripts;
1279 glyphs_requested = *input->sets.glyphs;
1280 drop_tables = *input->sets.drop_tables;
1281 no_subset_tables = *input->sets.no_subset_tables;
1282 source = hb_face_reference (face);
1283 dest = hb_face_builder_create ();
1284
1285 codepoint_to_glyph = hb_map_create ();
1286 glyph_map = hb_map_create ();
1287 reverse_glyph_map = hb_map_create ();
1288
1289 gsub_insert_catch_all_feature_variation_rec = false;
1290 gpos_insert_catch_all_feature_variation_rec = false;
1291 gdef_varstore_inner_maps.init ();
1292
1293 user_axes_location = input->axes_location;
1294 all_axes_pinned = false;
1295 pinned_at_default = true;
1296 has_gdef_varstore = false;
1297
1298 #ifdef HB_EXPERIMENTAL_API
1299 for (auto _ : input->name_table_overrides)
1300 {
1301 hb_bytes_t name_bytes = _.second;
1302 unsigned len = name_bytes.length;
1303 char *name_str = (char *) hb_malloc (len);
1304 if (unlikely (!check_success (name_str)))
1305 break;
1306
1307 hb_memcpy (name_str, name_bytes.arrayZ, len);
1308 name_table_overrides.set (_.first, hb_bytes_t (name_str, len));
1309 }
1310 #endif
1311
1312 void* accel = hb_face_get_user_data(face, hb_subset_accelerator_t::user_data_key());
1313
1314 attach_accelerator_data = input->attach_accelerator_data;
1315 force_long_loca = input->force_long_loca;
1316 #ifdef HB_EXPERIMENTAL_API
1317 force_long_loca = force_long_loca || (flags & HB_SUBSET_FLAGS_IFTB_REQUIREMENTS);
1318 #endif
1319
1320 if (accel)
1321 accelerator = (hb_subset_accelerator_t*) accel;
1322
1323 if (unlikely (in_error ()))
1324 return;
1325
1326 #ifndef HB_NO_VAR
1327 _normalize_axes_location (face, this);
1328 #endif
1329
1330 _populate_unicodes_to_retain (input->sets.unicodes, input->sets.glyphs, this);
1331
1332 _populate_gids_to_retain (this, input->sets.drop_tables);
1333 if (unlikely (in_error ()))
1334 return;
1335
1336 if (!check_success(_create_old_gid_to_new_gid_map(
1337 face,
1338 input->flags & HB_SUBSET_FLAGS_RETAIN_GIDS,
1339 &_glyphset,
1340 &input->glyph_map,
1341 glyph_map,
1342 reverse_glyph_map,
1343 &new_to_old_gid_list,
1344 &_num_output_glyphs))) {
1345 return;
1346 }
1347
1348 _create_glyph_map_gsub (
1349 &_glyphset_gsub,
1350 glyph_map,
1351 &glyph_map_gsub);
1352
1353 // Now that we have old to new gid map update the unicode to new gid list.
1354 for (unsigned i = 0; i < unicode_to_new_gid_list.length; i++)
1355 {
1356 // Use raw array access for performance.
1357 unicode_to_new_gid_list.arrayZ[i].second =
1358 glyph_map->get(unicode_to_new_gid_list.arrayZ[i].second);
1359 }
1360
1361 bounds_width_vec.resize (_num_output_glyphs, false);
1362 for (auto &v : bounds_width_vec)
1363 v = 0xFFFFFFFF;
1364 bounds_height_vec.resize (_num_output_glyphs, false);
1365 for (auto &v : bounds_height_vec)
1366 v = 0xFFFFFFFF;
1367
1368 if (!drop_tables.has (HB_OT_TAG_GDEF))
1369 _remap_used_mark_sets (this, used_mark_sets_map);
1370
1371 #ifndef HB_NO_VAR
1372 #ifndef HB_NO_BASE
1373 if (!drop_tables.has (HB_OT_TAG_BASE))
1374 _collect_base_variation_indices (this);
1375 #endif
1376 #endif
1377
1378 if (unlikely (in_error ()))
1379 return;
1380
1381 #ifndef HB_NO_VAR
1382 _update_instance_metrics_map_from_cff2 (this);
1383 if (!check_success (_get_instance_glyphs_contour_points (this)))
1384 return;
1385 #endif
1386
1387 if (attach_accelerator_data)
1388 {
1389 inprogress_accelerator =
1390 hb_subset_accelerator_t::create (source,
1391 *codepoint_to_glyph,
1392 unicodes,
1393 has_seac);
1394
1395 check_success (inprogress_accelerator);
1396 }
1397
1398 #define HB_SUBSET_PLAN_MEMBER(Type, Name) check_success (!Name.in_error ());
1399 #include "hb-subset-plan-member-list.hh"
1400 #undef HB_SUBSET_PLAN_MEMBER
1401 }
1402
~hb_subset_plan_t()1403 hb_subset_plan_t::~hb_subset_plan_t()
1404 {
1405 hb_face_destroy (dest);
1406
1407 hb_map_destroy (codepoint_to_glyph);
1408 hb_map_destroy (glyph_map);
1409 hb_map_destroy (reverse_glyph_map);
1410 #ifndef HB_NO_SUBSET_CFF
1411 cff1_accel.fini ();
1412 cff2_accel.fini ();
1413 #endif
1414 hb_face_destroy (source);
1415
1416 #ifdef HB_EXPERIMENTAL_API
1417 for (auto _ : name_table_overrides.iter_ref ())
1418 _.second.fini ();
1419 #endif
1420
1421 if (inprogress_accelerator)
1422 hb_subset_accelerator_t::destroy ((void*) inprogress_accelerator);
1423 }
1424
1425
1426 /**
1427 * hb_subset_plan_create_or_fail:
1428 * @face: font face to create the plan for.
1429 * @input: a #hb_subset_input_t input.
1430 *
1431 * Computes a plan for subsetting the supplied face according
1432 * to a provided input. The plan describes
1433 * which tables and glyphs should be retained.
1434 *
1435 * Return value: (transfer full): New subset plan. Destroy with
1436 * hb_subset_plan_destroy(). If there is a failure creating the plan
1437 * nullptr will be returned.
1438 *
1439 * Since: 4.0.0
1440 **/
1441 hb_subset_plan_t *
hb_subset_plan_create_or_fail(hb_face_t * face,const hb_subset_input_t * input)1442 hb_subset_plan_create_or_fail (hb_face_t *face,
1443 const hb_subset_input_t *input)
1444 {
1445 hb_subset_plan_t *plan;
1446 if (unlikely (!(plan = hb_object_create<hb_subset_plan_t> (face, input))))
1447 return nullptr;
1448
1449 if (unlikely (plan->in_error ()))
1450 {
1451 hb_subset_plan_destroy (plan);
1452 return nullptr;
1453 }
1454
1455 return plan;
1456 }
1457
1458 /**
1459 * hb_subset_plan_destroy:
1460 * @plan: a #hb_subset_plan_t
1461 *
1462 * Decreases the reference count on @plan, and if it reaches zero, destroys
1463 * @plan, freeing all memory.
1464 *
1465 * Since: 4.0.0
1466 **/
1467 void
hb_subset_plan_destroy(hb_subset_plan_t * plan)1468 hb_subset_plan_destroy (hb_subset_plan_t *plan)
1469 {
1470 if (!hb_object_destroy (plan)) return;
1471
1472 hb_free (plan);
1473 }
1474
1475 /**
1476 * hb_subset_plan_old_to_new_glyph_mapping:
1477 * @plan: a subsetting plan.
1478 *
1479 * Returns the mapping between glyphs in the original font to glyphs in the
1480 * subset that will be produced by @plan
1481 *
1482 * Return value: (transfer none):
1483 * A pointer to the #hb_map_t of the mapping.
1484 *
1485 * Since: 4.0.0
1486 **/
1487 hb_map_t *
hb_subset_plan_old_to_new_glyph_mapping(const hb_subset_plan_t * plan)1488 hb_subset_plan_old_to_new_glyph_mapping (const hb_subset_plan_t *plan)
1489 {
1490 return plan->glyph_map;
1491 }
1492
1493 /**
1494 * hb_subset_plan_new_to_old_glyph_mapping:
1495 * @plan: a subsetting plan.
1496 *
1497 * Returns the mapping between glyphs in the subset that will be produced by
1498 * @plan and the glyph in the original font.
1499 *
1500 * Return value: (transfer none):
1501 * A pointer to the #hb_map_t of the mapping.
1502 *
1503 * Since: 4.0.0
1504 **/
1505 hb_map_t *
hb_subset_plan_new_to_old_glyph_mapping(const hb_subset_plan_t * plan)1506 hb_subset_plan_new_to_old_glyph_mapping (const hb_subset_plan_t *plan)
1507 {
1508 return plan->reverse_glyph_map;
1509 }
1510
1511 /**
1512 * hb_subset_plan_unicode_to_old_glyph_mapping:
1513 * @plan: a subsetting plan.
1514 *
1515 * Returns the mapping between codepoints in the original font and the
1516 * associated glyph id in the original font.
1517 *
1518 * Return value: (transfer none):
1519 * A pointer to the #hb_map_t of the mapping.
1520 *
1521 * Since: 4.0.0
1522 **/
1523 hb_map_t *
hb_subset_plan_unicode_to_old_glyph_mapping(const hb_subset_plan_t * plan)1524 hb_subset_plan_unicode_to_old_glyph_mapping (const hb_subset_plan_t *plan)
1525 {
1526 return plan->codepoint_to_glyph;
1527 }
1528
1529 /**
1530 * hb_subset_plan_reference: (skip)
1531 * @plan: a #hb_subset_plan_t object.
1532 *
1533 * Increases the reference count on @plan.
1534 *
1535 * Return value: @plan.
1536 *
1537 * Since: 4.0.0
1538 **/
1539 hb_subset_plan_t *
hb_subset_plan_reference(hb_subset_plan_t * plan)1540 hb_subset_plan_reference (hb_subset_plan_t *plan)
1541 {
1542 return hb_object_reference (plan);
1543 }
1544
1545 /**
1546 * hb_subset_plan_set_user_data: (skip)
1547 * @plan: a #hb_subset_plan_t object.
1548 * @key: The user-data key to set
1549 * @data: A pointer to the user data
1550 * @destroy: (nullable): A callback to call when @data is not needed anymore
1551 * @replace: Whether to replace an existing data with the same key
1552 *
1553 * Attaches a user-data key/data pair to the given subset plan object.
1554 *
1555 * Return value: `true` if success, `false` otherwise
1556 *
1557 * Since: 4.0.0
1558 **/
1559 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)1560 hb_subset_plan_set_user_data (hb_subset_plan_t *plan,
1561 hb_user_data_key_t *key,
1562 void *data,
1563 hb_destroy_func_t destroy,
1564 hb_bool_t replace)
1565 {
1566 return hb_object_set_user_data (plan, key, data, destroy, replace);
1567 }
1568
1569 /**
1570 * hb_subset_plan_get_user_data: (skip)
1571 * @plan: a #hb_subset_plan_t object.
1572 * @key: The user-data key to query
1573 *
1574 * Fetches the user data associated with the specified key,
1575 * attached to the specified subset plan object.
1576 *
1577 * Return value: (transfer none): A pointer to the user data
1578 *
1579 * Since: 4.0.0
1580 **/
1581 void *
hb_subset_plan_get_user_data(const hb_subset_plan_t * plan,hb_user_data_key_t * key)1582 hb_subset_plan_get_user_data (const hb_subset_plan_t *plan,
1583 hb_user_data_key_t *key)
1584 {
1585 return hb_object_get_user_data (plan, key);
1586 }
1587