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 if (glyphs->is_empty () && unicodes->get_population () < size_threshold)
683 {
684
685 const hb_map_t* unicode_to_gid = nullptr;
686 if (plan->accelerator)
687 unicode_to_gid = &plan->accelerator->unicode_to_gid;
688
689 // This is approach to collection is faster, but can only be used if glyphs
690 // are not being explicitly added to the subset and the input unicodes set is
691 // not excessively large (eg. an inverted set).
692 plan->unicode_to_new_gid_list.alloc (unicodes->get_population ());
693 if (!unicode_to_gid) {
694 _fill_unicode_and_glyph_map(plan, unicodes->iter(), [&] (hb_codepoint_t cp) {
695 hb_codepoint_t gid;
696 if (!cmap.get_nominal_glyph (cp, &gid)) {
697 return HB_MAP_VALUE_INVALID;
698 }
699 return gid;
700 });
701 } else {
702 // Use in memory unicode to gid map it's faster then looking up from
703 // the map. This code is mostly duplicated from above to avoid doing
704 // conditionals on the presence of the unicode_to_gid map each
705 // iteration.
706 _fill_unicode_and_glyph_map(plan, unicodes->iter(), [&] (hb_codepoint_t cp) {
707 return unicode_to_gid->get (cp);
708 });
709 }
710 }
711 else
712 {
713 // This approach is slower, but can handle adding in glyphs to the subset and will match
714 // them with cmap entries.
715
716 hb_map_t unicode_glyphid_map_storage;
717 hb_set_t cmap_unicodes_storage;
718 const hb_map_t* unicode_glyphid_map = &unicode_glyphid_map_storage;
719 const hb_set_t* cmap_unicodes = &cmap_unicodes_storage;
720
721 if (!plan->accelerator) {
722 cmap.collect_mapping (&cmap_unicodes_storage, &unicode_glyphid_map_storage);
723 plan->unicode_to_new_gid_list.alloc (hb_min(unicodes->get_population ()
724 + glyphs->get_population (),
725 cmap_unicodes->get_population ()));
726 } else {
727 unicode_glyphid_map = &plan->accelerator->unicode_to_gid;
728 cmap_unicodes = &plan->accelerator->unicodes;
729 }
730
731 if (plan->accelerator &&
732 unicodes->get_population () < cmap_unicodes->get_population () &&
733 glyphs->get_population () < cmap_unicodes->get_population ())
734 {
735 plan->codepoint_to_glyph->alloc (unicodes->get_population () + glyphs->get_population ());
736
737 auto &gid_to_unicodes = plan->accelerator->gid_to_unicodes;
738
739 for (hb_codepoint_t gid : *glyphs)
740 {
741 auto unicodes = gid_to_unicodes.get (gid);
742 _fill_unicode_and_glyph_map<true>(plan, unicodes, [&] (hb_codepoint_t cp) {
743 return gid;
744 },
745 [&] (hb_codepoint_t cp) {
746 return unicode_glyphid_map->get(cp);
747 });
748 }
749
750 _fill_unicode_and_glyph_map(plan, unicodes->iter(), [&] (hb_codepoint_t cp) {
751 /* Don't double-add entry. */
752 if (plan->codepoint_to_glyph->has (cp))
753 return HB_MAP_VALUE_INVALID;
754
755 return unicode_glyphid_map->get(cp);
756 },
757 [&] (hb_codepoint_t cp) {
758 return unicode_glyphid_map->get(cp);
759 });
760
761 plan->unicode_to_new_gid_list.qsort ();
762 }
763 else
764 {
765 plan->codepoint_to_glyph->alloc (cmap_unicodes->get_population ());
766 hb_codepoint_t first = HB_SET_VALUE_INVALID, last = HB_SET_VALUE_INVALID;
767 for (; cmap_unicodes->next_range (&first, &last); )
768 {
769 _fill_unicode_and_glyph_map(plan, hb_range(first, last + 1), [&] (hb_codepoint_t cp) {
770 hb_codepoint_t gid = (*unicode_glyphid_map)[cp];
771 if (!unicodes->has (cp) && !glyphs->has (gid))
772 return HB_MAP_VALUE_INVALID;
773 return gid;
774 },
775 [&] (hb_codepoint_t cp) {
776 return unicode_glyphid_map->get(cp);
777 });
778 }
779 }
780
781 /* Add gids which where requested, but not mapped in cmap */
782 unsigned num_glyphs = plan->source->get_num_glyphs ();
783 hb_codepoint_t first = HB_SET_VALUE_INVALID, last = HB_SET_VALUE_INVALID;
784 for (; glyphs->next_range (&first, &last); )
785 {
786 if (first >= num_glyphs)
787 break;
788 if (last >= num_glyphs)
789 last = num_glyphs - 1;
790 plan->_glyphset_gsub.add_range (first, last);
791 }
792 }
793
794 auto &arr = plan->unicode_to_new_gid_list;
795 if (arr.length)
796 {
797 plan->unicodes.add_sorted_array (&arr.arrayZ->first, arr.length, sizeof (*arr.arrayZ));
798 plan->_glyphset_gsub.add_array (&arr.arrayZ->second, arr.length, sizeof (*arr.arrayZ));
799 }
800 }
801
802 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)803 _glyf_add_gid_and_children (const OT::glyf_accelerator_t &glyf,
804 hb_codepoint_t gid,
805 hb_set_t *gids_to_retain,
806 int operation_count,
807 unsigned depth = 0)
808 {
809 /* Check if is already visited */
810 if (gids_to_retain->has (gid)) return operation_count;
811
812 gids_to_retain->add (gid);
813
814 if (unlikely (depth++ > HB_MAX_NESTING_LEVEL)) return operation_count;
815 if (unlikely (--operation_count < 0)) return operation_count;
816
817 auto glyph = glyf.glyph_for_gid (gid);
818
819 for (auto &item : glyph.get_composite_iterator ())
820 operation_count =
821 _glyf_add_gid_and_children (glyf,
822 item.get_gid (),
823 gids_to_retain,
824 operation_count,
825 depth);
826
827 return operation_count;
828 }
829
830 static void
_nameid_closure(hb_subset_plan_t * plan,hb_set_t * drop_tables)831 _nameid_closure (hb_subset_plan_t* plan,
832 hb_set_t* drop_tables)
833 {
834 #ifndef HB_NO_STYLE
835 plan->source->table.STAT->collect_name_ids (&plan->user_axes_location, &plan->name_ids);
836 #endif
837 #ifndef HB_NO_VAR
838 if (!plan->all_axes_pinned)
839 plan->source->table.fvar->collect_name_ids (&plan->user_axes_location, &plan->axes_old_index_tag_map, &plan->name_ids);
840 #endif
841 #ifndef HB_NO_COLOR
842 if (!drop_tables->has (HB_OT_TAG_CPAL))
843 plan->source->table.CPAL->collect_name_ids (&plan->colr_palettes, &plan->name_ids);
844 #endif
845
846 #ifndef HB_NO_SUBSET_LAYOUT
847 if (!drop_tables->has (HB_OT_TAG_GPOS))
848 {
849 hb_blob_ptr_t<GPOS> gpos = plan->source_table<GPOS> ();
850 gpos->collect_name_ids (&plan->gpos_features, &plan->name_ids);
851 gpos.destroy ();
852 }
853 if (!drop_tables->has (HB_OT_TAG_GSUB))
854 {
855 hb_blob_ptr_t<GSUB> gsub = plan->source_table<GSUB> ();
856 gsub->collect_name_ids (&plan->gsub_features, &plan->name_ids);
857 gsub.destroy ();
858 }
859 #endif
860 }
861
862 static void
_populate_gids_to_retain(hb_subset_plan_t * plan,hb_set_t * drop_tables)863 _populate_gids_to_retain (hb_subset_plan_t* plan,
864 hb_set_t* drop_tables)
865 {
866 OT::glyf_accelerator_t glyf (plan->source);
867 #ifndef HB_NO_SUBSET_CFF
868 // Note: we cannot use inprogress_accelerator here, since it has not been
869 // created yet. So in case of preprocessed-face (and otherwise), we do an
870 // extra sanitize pass here, which is not ideal.
871 OT::cff1::accelerator_subset_t stack_cff (plan->accelerator ? nullptr : plan->source);
872 const OT::cff1::accelerator_subset_t *cff (plan->accelerator ? plan->accelerator->cff1_accel.get () : &stack_cff);
873 #endif
874
875 plan->_glyphset_gsub.add (0); // Not-def
876
877 _cmap_closure (plan->source, &plan->unicodes, &plan->_glyphset_gsub);
878
879 #ifndef HB_NO_SUBSET_LAYOUT
880 if (!drop_tables->has (HB_OT_TAG_GSUB))
881 // closure all glyphs/lookups/features needed for GSUB substitutions.
882 _closure_glyphs_lookups_features<GSUB> (
883 plan,
884 &plan->_glyphset_gsub,
885 &plan->gsub_lookups,
886 &plan->gsub_features,
887 &plan->gsub_langsys,
888 &plan->gsub_feature_record_cond_idx_map,
889 &plan->gsub_feature_substitutes_map,
890 plan->gsub_old_features,
891 plan->gsub_old_feature_idx_tag_map);
892
893 if (!drop_tables->has (HB_OT_TAG_GPOS))
894 _closure_glyphs_lookups_features<GPOS> (
895 plan,
896 &plan->_glyphset_gsub,
897 &plan->gpos_lookups,
898 &plan->gpos_features,
899 &plan->gpos_langsys,
900 &plan->gpos_feature_record_cond_idx_map,
901 &plan->gpos_feature_substitutes_map,
902 plan->gpos_old_features,
903 plan->gpos_old_feature_idx_tag_map);
904 #endif
905 _remove_invalid_gids (&plan->_glyphset_gsub, plan->source->get_num_glyphs ());
906
907 plan->_glyphset_mathed = plan->_glyphset_gsub;
908 if (!drop_tables->has (HB_OT_TAG_MATH))
909 {
910 _math_closure (plan, &plan->_glyphset_mathed);
911 _remove_invalid_gids (&plan->_glyphset_mathed, plan->source->get_num_glyphs ());
912 }
913
914 hb_set_t cur_glyphset = plan->_glyphset_mathed;
915 if (!drop_tables->has (HB_OT_TAG_COLR))
916 {
917 _colr_closure (plan, &cur_glyphset);
918 _remove_invalid_gids (&cur_glyphset, plan->source->get_num_glyphs ());
919 }
920
921 plan->_glyphset_colred = cur_glyphset;
922
923 // XXX TODO VARC closure / subset
924
925 _nameid_closure (plan, drop_tables);
926 /* Populate a full set of glyphs to retain by adding all referenced
927 * composite glyphs. */
928 if (glyf.has_data ())
929 for (hb_codepoint_t gid : cur_glyphset)
930 _glyf_add_gid_and_children (glyf, gid, &plan->_glyphset,
931 cur_glyphset.get_population () * HB_MAX_COMPOSITE_OPERATIONS_PER_GLYPH);
932 else
933 plan->_glyphset.union_ (cur_glyphset);
934 #ifndef HB_NO_SUBSET_CFF
935 if (!plan->accelerator || plan->accelerator->has_seac)
936 {
937 bool has_seac = false;
938 if (cff->is_valid ())
939 for (hb_codepoint_t gid : cur_glyphset)
940 if (_add_cff_seac_components (*cff, gid, &plan->_glyphset))
941 has_seac = true;
942 plan->has_seac = has_seac;
943 }
944 #endif
945
946 _remove_invalid_gids (&plan->_glyphset, plan->source->get_num_glyphs ());
947
948 #ifndef HB_NO_VAR
949 if (!drop_tables->has (HB_OT_TAG_GDEF))
950 _collect_layout_variation_indices (plan);
951 #endif
952 }
953
954 static void
_create_glyph_map_gsub(const hb_set_t * glyph_set_gsub,const hb_map_t * glyph_map,hb_map_t * out)955 _create_glyph_map_gsub (const hb_set_t* glyph_set_gsub,
956 const hb_map_t* glyph_map,
957 hb_map_t* out)
958 {
959 out->alloc (glyph_set_gsub->get_population ());
960 + hb_iter (glyph_set_gsub)
961 | hb_map ([&] (hb_codepoint_t gid) {
962 return hb_codepoint_pair_t (gid, glyph_map->get (gid));
963 })
964 | hb_sink (out)
965 ;
966 }
967
968 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)969 _create_old_gid_to_new_gid_map (const hb_face_t *face,
970 bool retain_gids,
971 const hb_set_t *all_gids_to_retain,
972 const hb_map_t *requested_glyph_map,
973 hb_map_t *glyph_map, /* OUT */
974 hb_map_t *reverse_glyph_map, /* OUT */
975 hb_sorted_vector_t<hb_codepoint_pair_t> *new_to_old_gid_list /* OUT */,
976 unsigned int *num_glyphs /* OUT */)
977 {
978 unsigned pop = all_gids_to_retain->get_population ();
979 reverse_glyph_map->alloc (pop);
980 glyph_map->alloc (pop);
981 new_to_old_gid_list->alloc (pop);
982
983 if (*requested_glyph_map)
984 {
985 hb_set_t new_gids(requested_glyph_map->values());
986 if (new_gids.get_population() != requested_glyph_map->get_population())
987 {
988 DEBUG_MSG (SUBSET, nullptr, "The provided custom glyph mapping is not unique.");
989 return false;
990 }
991
992 if (retain_gids)
993 {
994 DEBUG_MSG (SUBSET, nullptr,
995 "HB_SUBSET_FLAGS_RETAIN_GIDS cannot be set if "
996 "a custom glyph mapping has been provided.");
997 return false;
998 }
999
1000 hb_codepoint_t max_glyph = 0;
1001 hb_set_t remaining;
1002 for (auto old_gid : all_gids_to_retain->iter ())
1003 {
1004 if (old_gid == 0) {
1005 new_to_old_gid_list->push (hb_pair<hb_codepoint_t, hb_codepoint_t> (0u, 0u));
1006 continue;
1007 }
1008
1009 hb_codepoint_t* new_gid;
1010 if (!requested_glyph_map->has (old_gid, &new_gid))
1011 {
1012 remaining.add(old_gid);
1013 continue;
1014 }
1015
1016 if (*new_gid > max_glyph)
1017 max_glyph = *new_gid;
1018 new_to_old_gid_list->push (hb_pair (*new_gid, old_gid));
1019 }
1020 new_to_old_gid_list->qsort ();
1021
1022 // Anything that wasn't mapped by the requested mapping should
1023 // be placed after the requested mapping.
1024 for (auto old_gid : remaining)
1025 new_to_old_gid_list->push (hb_pair (++max_glyph, old_gid));
1026
1027 *num_glyphs = max_glyph + 1;
1028 }
1029 else if (!retain_gids)
1030 {
1031 + hb_enumerate (hb_iter (all_gids_to_retain), (hb_codepoint_t) 0)
1032 | hb_sink (new_to_old_gid_list)
1033 ;
1034 *num_glyphs = new_to_old_gid_list->length;
1035 }
1036 else
1037 {
1038 + hb_iter (all_gids_to_retain)
1039 | hb_map ([] (hb_codepoint_t _) {
1040 return hb_codepoint_pair_t (_, _);
1041 })
1042 | hb_sink (new_to_old_gid_list)
1043 ;
1044
1045 hb_codepoint_t max_glyph = HB_SET_VALUE_INVALID;
1046 hb_set_previous (all_gids_to_retain, &max_glyph);
1047
1048 *num_glyphs = max_glyph + 1;
1049 }
1050
1051 reverse_glyph_map->alloc (reverse_glyph_map->get_population () + new_to_old_gid_list->length);
1052 + hb_iter (new_to_old_gid_list)
1053 | hb_sink (reverse_glyph_map)
1054 ;
1055 glyph_map->alloc (glyph_map->get_population () + new_to_old_gid_list->length);
1056 + hb_iter (new_to_old_gid_list)
1057 | hb_map (&hb_codepoint_pair_t::reverse)
1058 | hb_sink (glyph_map)
1059 ;
1060
1061 return true;
1062 }
1063
1064 #ifndef HB_NO_VAR
1065 static void
_normalize_axes_location(hb_face_t * face,hb_subset_plan_t * plan)1066 _normalize_axes_location (hb_face_t *face, hb_subset_plan_t *plan)
1067 {
1068 if (plan->user_axes_location.is_empty ())
1069 return;
1070
1071 hb_array_t<const OT::AxisRecord> axes = face->table.fvar->get_axes ();
1072 plan->normalized_coords.resize (axes.length);
1073
1074 bool has_avar = face->table.avar->has_data ();
1075 const OT::SegmentMaps *seg_maps = nullptr;
1076 unsigned avar_axis_count = 0;
1077 if (has_avar)
1078 {
1079 seg_maps = face->table.avar->get_segment_maps ();
1080 avar_axis_count = face->table.avar->get_axis_count();
1081 }
1082
1083 bool axis_not_pinned = false;
1084 unsigned old_axis_idx = 0, new_axis_idx = 0;
1085 for (const auto& axis : axes)
1086 {
1087 hb_tag_t axis_tag = axis.get_axis_tag ();
1088 plan->axes_old_index_tag_map.set (old_axis_idx, axis_tag);
1089
1090 if (!plan->user_axes_location.has (axis_tag) ||
1091 !plan->user_axes_location.get (axis_tag).is_point ())
1092 {
1093 axis_not_pinned = true;
1094 plan->axes_index_map.set (old_axis_idx, new_axis_idx);
1095 plan->axis_tags.push (axis_tag);
1096 new_axis_idx++;
1097 }
1098
1099 Triple *axis_range;
1100 if (plan->user_axes_location.has (axis_tag, &axis_range))
1101 {
1102 plan->axes_triple_distances.set (axis_tag, axis.get_triple_distances ());
1103
1104 int normalized_min = axis.normalize_axis_value (axis_range->minimum);
1105 int normalized_default = axis.normalize_axis_value (axis_range->middle);
1106 int normalized_max = axis.normalize_axis_value (axis_range->maximum);
1107
1108 if (has_avar && old_axis_idx < avar_axis_count)
1109 {
1110 normalized_min = seg_maps->map (normalized_min);
1111 normalized_default = seg_maps->map (normalized_default);
1112 normalized_max = seg_maps->map (normalized_max);
1113 }
1114 plan->axes_location.set (axis_tag, Triple (static_cast<double> (normalized_min / 16384.0),
1115 static_cast<double> (normalized_default / 16384.0),
1116 static_cast<double> (normalized_max / 16384.0)));
1117
1118 if (normalized_default != 0)
1119 plan->pinned_at_default = false;
1120
1121 plan->normalized_coords[old_axis_idx] = normalized_default;
1122 }
1123
1124 old_axis_idx++;
1125
1126 if (has_avar && old_axis_idx < avar_axis_count)
1127 seg_maps = &StructAfter<OT::SegmentMaps> (*seg_maps);
1128 }
1129 plan->all_axes_pinned = !axis_not_pinned;
1130 }
1131
1132 static void
_update_instance_metrics_map_from_cff2(hb_subset_plan_t * plan)1133 _update_instance_metrics_map_from_cff2 (hb_subset_plan_t *plan)
1134 {
1135 if (!plan->normalized_coords) return;
1136 OT::cff2::accelerator_t cff2 (plan->source);
1137 if (!cff2.is_valid ()) return;
1138
1139 hb_font_t *font = _get_hb_font_with_variations (plan);
1140 if (unlikely (!plan->check_success (font != nullptr)))
1141 {
1142 hb_font_destroy (font);
1143 return;
1144 }
1145
1146 hb_glyph_extents_t extents = {0x7FFF, -0x7FFF};
1147 OT::hmtx_accelerator_t _hmtx (plan->source);
1148 float *hvar_store_cache = nullptr;
1149 if (_hmtx.has_data () && _hmtx.var_table.get_length ())
1150 hvar_store_cache = _hmtx.var_table->get_var_store ().create_cache ();
1151
1152 OT::vmtx_accelerator_t _vmtx (plan->source);
1153 float *vvar_store_cache = nullptr;
1154 if (_vmtx.has_data () && _vmtx.var_table.get_length ())
1155 vvar_store_cache = _vmtx.var_table->get_var_store ().create_cache ();
1156
1157 for (auto p : *plan->glyph_map)
1158 {
1159 hb_codepoint_t old_gid = p.first;
1160 hb_codepoint_t new_gid = p.second;
1161 if (!cff2.get_extents (font, old_gid, &extents)) continue;
1162 bool has_bounds_info = true;
1163 if (extents.x_bearing == 0 && extents.width == 0 &&
1164 extents.height == 0 && extents.y_bearing == 0)
1165 has_bounds_info = false;
1166
1167 if (has_bounds_info)
1168 {
1169 plan->head_maxp_info.xMin = hb_min (plan->head_maxp_info.xMin, extents.x_bearing);
1170 plan->head_maxp_info.xMax = hb_max (plan->head_maxp_info.xMax, extents.x_bearing + extents.width);
1171 plan->head_maxp_info.yMax = hb_max (plan->head_maxp_info.yMax, extents.y_bearing);
1172 plan->head_maxp_info.yMin = hb_min (plan->head_maxp_info.yMin, extents.y_bearing + extents.height);
1173 }
1174
1175 if (_hmtx.has_data ())
1176 {
1177 int hori_aw = _hmtx.get_advance_without_var_unscaled (old_gid);
1178 if (_hmtx.var_table.get_length ())
1179 hori_aw += (int) roundf (_hmtx.var_table->get_advance_delta_unscaled (old_gid, font->coords, font->num_coords,
1180 hvar_store_cache));
1181 int lsb = extents.x_bearing;
1182 if (!has_bounds_info)
1183 {
1184 if (!_hmtx.get_leading_bearing_without_var_unscaled (old_gid, &lsb))
1185 continue;
1186 }
1187 plan->hmtx_map.set (new_gid, hb_pair ((unsigned) hori_aw, lsb));
1188 plan->bounds_width_vec[new_gid] = extents.width;
1189 }
1190
1191 if (_vmtx.has_data ())
1192 {
1193 int vert_aw = _vmtx.get_advance_without_var_unscaled (old_gid);
1194 if (_vmtx.var_table.get_length ())
1195 vert_aw += (int) roundf (_vmtx.var_table->get_advance_delta_unscaled (old_gid, font->coords, font->num_coords,
1196 vvar_store_cache));
1197
1198 int tsb = extents.y_bearing;
1199 if (!has_bounds_info)
1200 {
1201 if (!_vmtx.get_leading_bearing_without_var_unscaled (old_gid, &tsb))
1202 continue;
1203 }
1204 plan->vmtx_map.set (new_gid, hb_pair ((unsigned) vert_aw, tsb));
1205 plan->bounds_height_vec[new_gid] = extents.height;
1206 }
1207 }
1208 hb_font_destroy (font);
1209 if (hvar_store_cache)
1210 _hmtx.var_table->get_var_store ().destroy_cache (hvar_store_cache);
1211 if (vvar_store_cache)
1212 _vmtx.var_table->get_var_store ().destroy_cache (vvar_store_cache);
1213 }
1214
1215 static bool
_get_instance_glyphs_contour_points(hb_subset_plan_t * plan)1216 _get_instance_glyphs_contour_points (hb_subset_plan_t *plan)
1217 {
1218 /* contour_points vector only needed for updating gvar table (infer delta and
1219 * iup delta optimization) during partial instancing */
1220 if (plan->user_axes_location.is_empty () || plan->all_axes_pinned)
1221 return true;
1222
1223 OT::glyf_accelerator_t glyf (plan->source);
1224
1225 for (auto &_ : plan->new_to_old_gid_list)
1226 {
1227 hb_codepoint_t new_gid = _.first;
1228 contour_point_vector_t all_points;
1229 if (new_gid == 0 && !(plan->flags & HB_SUBSET_FLAGS_NOTDEF_OUTLINE))
1230 {
1231 if (unlikely (!plan->new_gid_contour_points_map.set (new_gid, all_points)))
1232 return false;
1233 continue;
1234 }
1235
1236 hb_codepoint_t old_gid = _.second;
1237 auto glyph = glyf.glyph_for_gid (old_gid);
1238 if (unlikely (!glyph.get_all_points_without_var (plan->source, all_points)))
1239 return false;
1240 if (unlikely (!plan->new_gid_contour_points_map.set (new_gid, all_points)))
1241 return false;
1242
1243 /* composite new gids are only needed by iup delta optimization */
1244 if ((plan->flags & HB_SUBSET_FLAGS_OPTIMIZE_IUP_DELTAS) && glyph.is_composite ())
1245 plan->composite_new_gids.add (new_gid);
1246 }
1247 return true;
1248 }
1249 #endif
1250
hb_subset_plan_t(hb_face_t * face,const hb_subset_input_t * input)1251 hb_subset_plan_t::hb_subset_plan_t (hb_face_t *face,
1252 const hb_subset_input_t *input)
1253 {
1254 successful = true;
1255 flags = input->flags;
1256
1257 unicode_to_new_gid_list.init ();
1258
1259 name_ids = *input->sets.name_ids;
1260 name_languages = *input->sets.name_languages;
1261 layout_features = *input->sets.layout_features;
1262 layout_scripts = *input->sets.layout_scripts;
1263 glyphs_requested = *input->sets.glyphs;
1264 drop_tables = *input->sets.drop_tables;
1265 no_subset_tables = *input->sets.no_subset_tables;
1266 source = hb_face_reference (face);
1267 dest = hb_face_builder_create ();
1268
1269 codepoint_to_glyph = hb_map_create ();
1270 glyph_map = hb_map_create ();
1271 reverse_glyph_map = hb_map_create ();
1272
1273 gsub_insert_catch_all_feature_variation_rec = false;
1274 gpos_insert_catch_all_feature_variation_rec = false;
1275 gdef_varstore_inner_maps.init ();
1276
1277 user_axes_location = input->axes_location;
1278 all_axes_pinned = false;
1279 pinned_at_default = true;
1280 has_gdef_varstore = false;
1281
1282 #ifdef HB_EXPERIMENTAL_API
1283 for (auto _ : input->name_table_overrides)
1284 {
1285 hb_bytes_t name_bytes = _.second;
1286 unsigned len = name_bytes.length;
1287 char *name_str = (char *) hb_malloc (len);
1288 if (unlikely (!check_success (name_str)))
1289 break;
1290
1291 hb_memcpy (name_str, name_bytes.arrayZ, len);
1292 name_table_overrides.set (_.first, hb_bytes_t (name_str, len));
1293 }
1294 #endif
1295
1296 void* accel = hb_face_get_user_data(face, hb_subset_accelerator_t::user_data_key());
1297
1298 attach_accelerator_data = input->attach_accelerator_data;
1299 force_long_loca = input->force_long_loca;
1300 #ifdef HB_EXPERIMENTAL_API
1301 force_long_loca = force_long_loca || (flags & HB_SUBSET_FLAGS_IFTB_REQUIREMENTS);
1302 #endif
1303
1304 if (accel)
1305 accelerator = (hb_subset_accelerator_t*) accel;
1306
1307 if (unlikely (in_error ()))
1308 return;
1309
1310 #ifndef HB_NO_VAR
1311 _normalize_axes_location (face, this);
1312 #endif
1313
1314 _populate_unicodes_to_retain (input->sets.unicodes, input->sets.glyphs, this);
1315
1316 _populate_gids_to_retain (this, input->sets.drop_tables);
1317 if (unlikely (in_error ()))
1318 return;
1319
1320 if (!check_success(_create_old_gid_to_new_gid_map(
1321 face,
1322 input->flags & HB_SUBSET_FLAGS_RETAIN_GIDS,
1323 &_glyphset,
1324 &input->glyph_map,
1325 glyph_map,
1326 reverse_glyph_map,
1327 &new_to_old_gid_list,
1328 &_num_output_glyphs))) {
1329 return;
1330 }
1331
1332 _create_glyph_map_gsub (
1333 &_glyphset_gsub,
1334 glyph_map,
1335 &glyph_map_gsub);
1336
1337 // Now that we have old to new gid map update the unicode to new gid list.
1338 for (unsigned i = 0; i < unicode_to_new_gid_list.length; i++)
1339 {
1340 // Use raw array access for performance.
1341 unicode_to_new_gid_list.arrayZ[i].second =
1342 glyph_map->get(unicode_to_new_gid_list.arrayZ[i].second);
1343 }
1344
1345 bounds_width_vec.resize (_num_output_glyphs, false);
1346 for (auto &v : bounds_width_vec)
1347 v = 0xFFFFFFFF;
1348 bounds_height_vec.resize (_num_output_glyphs, false);
1349 for (auto &v : bounds_height_vec)
1350 v = 0xFFFFFFFF;
1351
1352 if (!drop_tables.has (HB_OT_TAG_GDEF))
1353 _remap_used_mark_sets (this, used_mark_sets_map);
1354
1355 #ifndef HB_NO_VAR
1356 #ifndef HB_NO_BASE
1357 if (!drop_tables.has (HB_OT_TAG_BASE))
1358 _collect_base_variation_indices (this);
1359 #endif
1360 #endif
1361
1362 if (unlikely (in_error ()))
1363 return;
1364
1365 #ifndef HB_NO_VAR
1366 _update_instance_metrics_map_from_cff2 (this);
1367 if (!check_success (_get_instance_glyphs_contour_points (this)))
1368 return;
1369 #endif
1370
1371 if (attach_accelerator_data)
1372 {
1373 inprogress_accelerator =
1374 hb_subset_accelerator_t::create (source,
1375 *codepoint_to_glyph,
1376 unicodes,
1377 has_seac);
1378
1379 check_success (inprogress_accelerator);
1380 }
1381
1382 #define HB_SUBSET_PLAN_MEMBER(Type, Name) check_success (!Name.in_error ());
1383 #include "hb-subset-plan-member-list.hh"
1384 #undef HB_SUBSET_PLAN_MEMBER
1385 }
1386
~hb_subset_plan_t()1387 hb_subset_plan_t::~hb_subset_plan_t()
1388 {
1389 hb_face_destroy (dest);
1390
1391 hb_map_destroy (codepoint_to_glyph);
1392 hb_map_destroy (glyph_map);
1393 hb_map_destroy (reverse_glyph_map);
1394 #ifndef HB_NO_SUBSET_CFF
1395 cff1_accel.fini ();
1396 cff2_accel.fini ();
1397 #endif
1398 hb_face_destroy (source);
1399
1400 #ifdef HB_EXPERIMENTAL_API
1401 for (auto _ : name_table_overrides.iter_ref ())
1402 _.second.fini ();
1403 #endif
1404
1405 if (inprogress_accelerator)
1406 hb_subset_accelerator_t::destroy ((void*) inprogress_accelerator);
1407 }
1408
1409
1410 /**
1411 * hb_subset_plan_create_or_fail:
1412 * @face: font face to create the plan for.
1413 * @input: a #hb_subset_input_t input.
1414 *
1415 * Computes a plan for subsetting the supplied face according
1416 * to a provided input. The plan describes
1417 * which tables and glyphs should be retained.
1418 *
1419 * Return value: (transfer full): New subset plan. Destroy with
1420 * hb_subset_plan_destroy(). If there is a failure creating the plan
1421 * nullptr will be returned.
1422 *
1423 * Since: 4.0.0
1424 **/
1425 hb_subset_plan_t *
hb_subset_plan_create_or_fail(hb_face_t * face,const hb_subset_input_t * input)1426 hb_subset_plan_create_or_fail (hb_face_t *face,
1427 const hb_subset_input_t *input)
1428 {
1429 hb_subset_plan_t *plan;
1430 if (unlikely (!(plan = hb_object_create<hb_subset_plan_t> (face, input))))
1431 return nullptr;
1432
1433 if (unlikely (plan->in_error ()))
1434 {
1435 hb_subset_plan_destroy (plan);
1436 return nullptr;
1437 }
1438
1439 return plan;
1440 }
1441
1442 /**
1443 * hb_subset_plan_destroy:
1444 * @plan: a #hb_subset_plan_t
1445 *
1446 * Decreases the reference count on @plan, and if it reaches zero, destroys
1447 * @plan, freeing all memory.
1448 *
1449 * Since: 4.0.0
1450 **/
1451 void
hb_subset_plan_destroy(hb_subset_plan_t * plan)1452 hb_subset_plan_destroy (hb_subset_plan_t *plan)
1453 {
1454 if (!hb_object_destroy (plan)) return;
1455
1456 hb_free (plan);
1457 }
1458
1459 /**
1460 * hb_subset_plan_old_to_new_glyph_mapping:
1461 * @plan: a subsetting plan.
1462 *
1463 * Returns the mapping between glyphs in the original font to glyphs in the
1464 * subset that will be produced by @plan
1465 *
1466 * Return value: (transfer none):
1467 * A pointer to the #hb_map_t of the mapping.
1468 *
1469 * Since: 4.0.0
1470 **/
1471 hb_map_t *
hb_subset_plan_old_to_new_glyph_mapping(const hb_subset_plan_t * plan)1472 hb_subset_plan_old_to_new_glyph_mapping (const hb_subset_plan_t *plan)
1473 {
1474 return plan->glyph_map;
1475 }
1476
1477 /**
1478 * hb_subset_plan_new_to_old_glyph_mapping:
1479 * @plan: a subsetting plan.
1480 *
1481 * Returns the mapping between glyphs in the subset that will be produced by
1482 * @plan and the glyph in the original font.
1483 *
1484 * Return value: (transfer none):
1485 * A pointer to the #hb_map_t of the mapping.
1486 *
1487 * Since: 4.0.0
1488 **/
1489 hb_map_t *
hb_subset_plan_new_to_old_glyph_mapping(const hb_subset_plan_t * plan)1490 hb_subset_plan_new_to_old_glyph_mapping (const hb_subset_plan_t *plan)
1491 {
1492 return plan->reverse_glyph_map;
1493 }
1494
1495 /**
1496 * hb_subset_plan_unicode_to_old_glyph_mapping:
1497 * @plan: a subsetting plan.
1498 *
1499 * Returns the mapping between codepoints in the original font and the
1500 * associated glyph id in the original font.
1501 *
1502 * Return value: (transfer none):
1503 * A pointer to the #hb_map_t of the mapping.
1504 *
1505 * Since: 4.0.0
1506 **/
1507 hb_map_t *
hb_subset_plan_unicode_to_old_glyph_mapping(const hb_subset_plan_t * plan)1508 hb_subset_plan_unicode_to_old_glyph_mapping (const hb_subset_plan_t *plan)
1509 {
1510 return plan->codepoint_to_glyph;
1511 }
1512
1513 /**
1514 * hb_subset_plan_reference: (skip)
1515 * @plan: a #hb_subset_plan_t object.
1516 *
1517 * Increases the reference count on @plan.
1518 *
1519 * Return value: @plan.
1520 *
1521 * Since: 4.0.0
1522 **/
1523 hb_subset_plan_t *
hb_subset_plan_reference(hb_subset_plan_t * plan)1524 hb_subset_plan_reference (hb_subset_plan_t *plan)
1525 {
1526 return hb_object_reference (plan);
1527 }
1528
1529 /**
1530 * hb_subset_plan_set_user_data: (skip)
1531 * @plan: a #hb_subset_plan_t object.
1532 * @key: The user-data key to set
1533 * @data: A pointer to the user data
1534 * @destroy: (nullable): A callback to call when @data is not needed anymore
1535 * @replace: Whether to replace an existing data with the same key
1536 *
1537 * Attaches a user-data key/data pair to the given subset plan object.
1538 *
1539 * Return value: `true` if success, `false` otherwise
1540 *
1541 * Since: 4.0.0
1542 **/
1543 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)1544 hb_subset_plan_set_user_data (hb_subset_plan_t *plan,
1545 hb_user_data_key_t *key,
1546 void *data,
1547 hb_destroy_func_t destroy,
1548 hb_bool_t replace)
1549 {
1550 return hb_object_set_user_data (plan, key, data, destroy, replace);
1551 }
1552
1553 /**
1554 * hb_subset_plan_get_user_data: (skip)
1555 * @plan: a #hb_subset_plan_t object.
1556 * @key: The user-data key to query
1557 *
1558 * Fetches the user data associated with the specified key,
1559 * attached to the specified subset plan object.
1560 *
1561 * Return value: (transfer none): A pointer to the user data
1562 *
1563 * Since: 4.0.0
1564 **/
1565 void *
hb_subset_plan_get_user_data(const hb_subset_plan_t * plan,hb_user_data_key_t * key)1566 hb_subset_plan_get_user_data (const hb_subset_plan_t *plan,
1567 hb_user_data_key_t *key)
1568 {
1569 return hb_object_get_user_data (plan, key);
1570 }
1571