• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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-gdef-table.hh"
36 #include "hb-ot-layout-gpos-table.hh"
37 #include "hb-ot-layout-gsub-table.hh"
38 #include "hb-ot-cff1-table.hh"
39 #include "hb-ot-color-colr-table.hh"
40 #include "hb-ot-color-colrv1-closure.hh"
41 #include "hb-ot-var-fvar-table.hh"
42 #include "hb-ot-var-avar-table.hh"
43 #include "hb-ot-stat-table.hh"
44 #include "hb-ot-math-table.hh"
45 
46 using OT::Layout::GSUB;
47 using OT::Layout::GPOS;
48 
49 typedef hb_hashmap_t<unsigned, hb::unique_ptr<hb_set_t>> script_langsys_map;
50 #ifndef HB_NO_SUBSET_CFF
51 static inline bool
_add_cff_seac_components(const OT::cff1::accelerator_t & cff,hb_codepoint_t gid,hb_set_t * gids_to_retain)52 _add_cff_seac_components (const OT::cff1::accelerator_t &cff,
53 			  hb_codepoint_t gid,
54 			  hb_set_t *gids_to_retain)
55 {
56   hb_codepoint_t base_gid, accent_gid;
57   if (cff.get_seac_components (gid, &base_gid, &accent_gid))
58   {
59     gids_to_retain->add (base_gid);
60     gids_to_retain->add (accent_gid);
61     return true;
62   }
63   return false;
64 }
65 #endif
66 
67 static void
_remap_palette_indexes(const hb_set_t * palette_indexes,hb_map_t * mapping)68 _remap_palette_indexes (const hb_set_t *palette_indexes,
69 			hb_map_t       *mapping /* OUT */)
70 {
71   unsigned new_idx = 0;
72   for (unsigned palette_index : palette_indexes->iter ())
73   {
74     if (palette_index == 0xFFFF)
75     {
76       mapping->set (palette_index, palette_index);
77       continue;
78     }
79     mapping->set (palette_index, new_idx);
80     new_idx++;
81   }
82 }
83 
84 static void
_remap_indexes(const hb_set_t * indexes,hb_map_t * mapping)85 _remap_indexes (const hb_set_t *indexes,
86 		hb_map_t       *mapping /* OUT */)
87 {
88   for (auto _ : + hb_enumerate (indexes->iter ()))
89     mapping->set (_.second, _.first);
90 
91 }
92 
93 #ifndef HB_NO_SUBSET_LAYOUT
94 
95 /*
96  * Removes all tags from 'tags' that are not in filter. Additionally eliminates any duplicates.
97  * Returns true if anything was removed (not including duplicates).
98  */
_filter_tag_list(hb_vector_t<hb_tag_t> * tags,const hb_set_t * filter)99 static bool _filter_tag_list(hb_vector_t<hb_tag_t>* tags, /* IN/OUT */
100                              const hb_set_t* filter)
101 {
102   hb_vector_t<hb_tag_t> out;
103   out.alloc (tags->get_size() + 1); // +1 is to allocate room for the null terminator.
104 
105   bool removed = false;
106   hb_set_t visited;
107 
108   for (hb_tag_t tag : *tags)
109   {
110     if (!tag) continue;
111     if (visited.has (tag)) continue;
112 
113     if (!filter->has (tag))
114     {
115       removed = true;
116       continue;
117     }
118 
119     visited.add (tag);
120     out.push (tag);
121   }
122 
123   // The collect function needs a null element to signal end of the array.
124   out.push (HB_TAG_NONE);
125 
126   hb_swap (out, *tags);
127   return removed;
128 }
129 
130 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)131 static void _collect_layout_indices (hb_subset_plan_t     *plan,
132                                      const T&              table,
133                                      hb_set_t		  *lookup_indices, /* OUT */
134                                      hb_set_t		  *feature_indices, /* OUT */
135                                      hb_hashmap_t<unsigned, hb::shared_ptr<hb_set_t>> *feature_record_cond_idx_map, /* OUT */
136                                      hb_hashmap_t<unsigned, const OT::Feature*> *feature_substitutes_map /* OUT */)
137 {
138   unsigned num_features = table.get_feature_count ();
139   hb_vector_t<hb_tag_t> features;
140   if (!plan->check_success (features.resize (num_features))) return;
141   table.get_feature_tags (0, &num_features, features.arrayZ);
142   bool retain_all_features = !_filter_tag_list (&features, plan->layout_features);
143 
144   unsigned num_scripts = table.get_script_count ();
145   hb_vector_t<hb_tag_t> scripts;
146   if (!plan->check_success (scripts.resize (num_scripts))) return;
147   table.get_script_tags (0, &num_scripts, scripts.arrayZ);
148   bool retain_all_scripts = !_filter_tag_list (&scripts, plan->layout_scripts);
149 
150   if (!plan->check_success (!features.in_error ()) || !features
151       || !plan->check_success (!scripts.in_error ()) || !scripts)
152     return;
153 
154   hb_ot_layout_collect_features (plan->source,
155                                  T::tableTag,
156                                  retain_all_scripts ? nullptr : scripts.arrayZ,
157                                  nullptr,
158                                  retain_all_features ? nullptr : features.arrayZ,
159                                  feature_indices);
160 
161 #ifndef HB_NO_VAR
162   // collect feature substitutes with variations
163   if (!plan->user_axes_location->is_empty ())
164   {
165     hb_hashmap_t<hb::shared_ptr<hb_map_t>, unsigned> conditionset_map;
166     OT::hb_collect_feature_substitutes_with_var_context_t c =
167     {
168       plan->axes_old_index_tag_map,
169       plan->axes_location,
170       feature_record_cond_idx_map,
171       feature_substitutes_map,
172       feature_indices,
173       true,
174       0,
175       &conditionset_map
176     };
177     table.collect_feature_substitutes_with_variations (&c);
178   }
179 #endif
180 
181   for (unsigned feature_index : *feature_indices)
182   {
183     const OT::Feature* f = &(table.get_feature (feature_index));
184     const OT::Feature **p = nullptr;
185     if (feature_substitutes_map->has (feature_index, &p))
186       f = *p;
187 
188     f->add_lookup_indexes_to (lookup_indices);
189   }
190 
191   table.feature_variation_collect_lookups (feature_indices, feature_substitutes_map, lookup_indices);
192 }
193 
194 
195 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)196 _GSUBGPOS_find_duplicate_features (const OT::GSUBGPOS &g,
197 				   const hb_map_t *lookup_indices,
198 				   const hb_set_t *feature_indices,
199 				   const hb_hashmap_t<unsigned, const OT::Feature*> *feature_substitutes_map,
200 				   hb_map_t *duplicate_feature_map /* OUT */)
201 {
202   if (feature_indices->is_empty ()) return;
203   hb_hashmap_t<hb_tag_t, hb::unique_ptr<hb_set_t>> unique_features;
204   //find out duplicate features after subset
205   for (unsigned i : feature_indices->iter ())
206   {
207     hb_tag_t t = g.get_feature_tag (i);
208     if (t == HB_MAP_VALUE_INVALID) continue;
209     if (!unique_features.has (t))
210     {
211       if (unlikely (!unique_features.set (t, hb::unique_ptr<hb_set_t> {hb_set_create ()})))
212 	return;
213       if (unique_features.has (t))
214 	unique_features.get (t)->add (i);
215       duplicate_feature_map->set (i, i);
216       continue;
217     }
218 
219     bool found = false;
220 
221     hb_set_t* same_tag_features = unique_features.get (t);
222     for (unsigned other_f_index : same_tag_features->iter ())
223     {
224       const OT::Feature* f = &(g.get_feature (i));
225       const OT::Feature **p = nullptr;
226       if (feature_substitutes_map->has (i, &p))
227         f = *p;
228 
229       const OT::Feature* other_f = &(g.get_feature (other_f_index));
230       if (feature_substitutes_map->has (other_f_index, &p))
231         f = *p;
232 
233       auto f_iter =
234       + hb_iter (f->lookupIndex)
235       | hb_filter (lookup_indices)
236       ;
237 
238       auto other_f_iter =
239       + hb_iter (other_f->lookupIndex)
240       | hb_filter (lookup_indices)
241       ;
242 
243       bool is_equal = true;
244       for (; f_iter && other_f_iter; f_iter++, other_f_iter++)
245       {
246 	unsigned a = *f_iter;
247 	unsigned b = *other_f_iter;
248 	if (a != b) { is_equal = false; break; }
249       }
250 
251       if (is_equal == false || f_iter || other_f_iter) continue;
252 
253       found = true;
254       duplicate_feature_map->set (i, other_f_index);
255       break;
256     }
257 
258     if (found == false)
259     {
260       same_tag_features->add (i);
261       duplicate_feature_map->set (i, i);
262     }
263   }
264 }
265 
266 template <typename T>
267 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)268 _closure_glyphs_lookups_features (hb_subset_plan_t   *plan,
269 				  hb_set_t	     *gids_to_retain,
270 				  hb_map_t	     *lookups,
271 				  hb_map_t	     *features,
272 				  script_langsys_map *langsys_map,
273 				  hb_hashmap_t<unsigned, hb::shared_ptr<hb_set_t>> *feature_record_cond_idx_map,
274 				  hb_hashmap_t<unsigned, const OT::Feature*> *feature_substitutes_map)
275 {
276   hb_blob_ptr_t<T> table = plan->source_table<T> ();
277   hb_tag_t table_tag = table->tableTag;
278   hb_set_t lookup_indices, feature_indices;
279   _collect_layout_indices<T> (plan,
280                               *table,
281                               &lookup_indices,
282                               &feature_indices,
283                               feature_record_cond_idx_map,
284                               feature_substitutes_map);
285 
286   if (table_tag == HB_OT_TAG_GSUB)
287     hb_ot_layout_lookups_substitute_closure (plan->source,
288                                              &lookup_indices,
289 					     gids_to_retain);
290   table->closure_lookups (plan->source,
291 			  gids_to_retain,
292                           &lookup_indices);
293   _remap_indexes (&lookup_indices, lookups);
294 
295   // prune features
296   table->prune_features (lookups,
297                          plan->user_axes_location->is_empty () ? nullptr : feature_record_cond_idx_map,
298                          feature_substitutes_map,
299                          &feature_indices);
300   hb_map_t duplicate_feature_map;
301   _GSUBGPOS_find_duplicate_features (*table, lookups, &feature_indices, feature_substitutes_map, &duplicate_feature_map);
302 
303   feature_indices.clear ();
304   table->prune_langsys (&duplicate_feature_map, plan->layout_scripts, langsys_map, &feature_indices);
305   _remap_indexes (&feature_indices, features);
306 
307   table.destroy ();
308 }
309 
310 #endif
311 
312 #ifndef HB_NO_VAR
313 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)314 _generate_varstore_inner_maps (const hb_set_t& varidx_set,
315                                unsigned subtable_count,
316                                hb_vector_t<hb_inc_bimap_t> &inner_maps /* OUT */)
317 {
318   if (varidx_set.is_empty () || subtable_count == 0) return;
319 
320   inner_maps.resize (subtable_count);
321   for (unsigned idx : varidx_set)
322   {
323     uint16_t major = idx >> 16;
324     uint16_t minor = idx & 0xFFFF;
325 
326     if (major >= subtable_count)
327       continue;
328     inner_maps[major].add (minor);
329   }
330 }
331 
332 static inline hb_font_t*
_get_hb_font_with_variations(const hb_subset_plan_t * plan)333 _get_hb_font_with_variations (const hb_subset_plan_t *plan)
334 {
335   hb_font_t *font = hb_font_create (plan->source);
336 
337   hb_vector_t<hb_variation_t> vars;
338   vars.alloc (plan->user_axes_location->get_population ());
339 
340   for (auto _ : *plan->user_axes_location)
341   {
342     hb_variation_t var;
343     var.tag = _.first;
344     var.value = _.second;
345     vars.push (var);
346   }
347 
348 #ifndef HB_NO_VAR
349   hb_font_set_variations (font, vars.arrayZ, plan->user_axes_location->get_population ());
350 #endif
351   return font;
352 }
353 
354 static inline void
_collect_layout_variation_indices(hb_subset_plan_t * plan)355 _collect_layout_variation_indices (hb_subset_plan_t* plan)
356 {
357   hb_blob_ptr_t<OT::GDEF> gdef = plan->source_table<OT::GDEF> ();
358   hb_blob_ptr_t<GPOS> gpos = plan->source_table<GPOS> ();
359 
360   if (!gdef->has_data ())
361   {
362     gdef.destroy ();
363     gpos.destroy ();
364     return;
365   }
366 
367   const OT::VariationStore *var_store = nullptr;
368   hb_set_t varidx_set;
369   hb_font_t *font = nullptr;
370   float *store_cache = nullptr;
371   bool collect_delta = plan->pinned_at_default ? false : true;
372   if (collect_delta)
373   {
374     font = _get_hb_font_with_variations (plan);
375     if (gdef->has_var_store ())
376     {
377       var_store = &(gdef->get_var_store ());
378       store_cache = var_store->create_cache ();
379     }
380   }
381 
382   OT::hb_collect_variation_indices_context_t c (&varidx_set,
383                                                 plan->layout_variation_idx_delta_map,
384                                                 font, var_store,
385                                                 plan->_glyphset_gsub,
386                                                 plan->gpos_lookups,
387                                                 store_cache);
388   gdef->collect_variation_indices (&c);
389 
390   if (hb_ot_layout_has_positioning (plan->source))
391     gpos->collect_variation_indices (&c);
392 
393   hb_font_destroy (font);
394   var_store->destroy_cache (store_cache);
395 
396   gdef->remap_layout_variation_indices (&varidx_set, plan->layout_variation_idx_delta_map);
397 
398   unsigned subtable_count = gdef->has_var_store () ? gdef->get_var_store ().get_sub_table_count () : 0;
399   _generate_varstore_inner_maps (varidx_set, subtable_count, plan->gdef_varstore_inner_maps);
400 
401   gdef.destroy ();
402   gpos.destroy ();
403 }
404 #endif
405 
406 static inline void
_cmap_closure(hb_face_t * face,const hb_set_t * unicodes,hb_set_t * glyphset)407 _cmap_closure (hb_face_t	   *face,
408 	       const hb_set_t	   *unicodes,
409 	       hb_set_t		   *glyphset)
410 {
411   OT::cmap::accelerator_t cmap (face);
412   cmap.table->closure_glyphs (unicodes, glyphset);
413 }
414 
_colr_closure(hb_face_t * face,hb_map_t * layers_map,hb_map_t * palettes_map,hb_set_t * glyphs_colred)415 static void _colr_closure (hb_face_t *face,
416                            hb_map_t *layers_map,
417                            hb_map_t *palettes_map,
418                            hb_set_t *glyphs_colred)
419 {
420   OT::COLR::accelerator_t colr (face);
421   if (!colr.is_valid ()) return;
422 
423   hb_set_t palette_indices, layer_indices;
424   // Collect all glyphs referenced by COLRv0
425   hb_set_t glyphset_colrv0;
426   for (hb_codepoint_t gid : *glyphs_colred)
427     colr.closure_glyphs (gid, &glyphset_colrv0);
428 
429   glyphs_colred->union_ (glyphset_colrv0);
430 
431   //closure for COLRv1
432   colr.closure_forV1 (glyphs_colred, &layer_indices, &palette_indices);
433 
434   colr.closure_V0palette_indices (glyphs_colred, &palette_indices);
435   _remap_indexes (&layer_indices, layers_map);
436   _remap_palette_indexes (&palette_indices, palettes_map);
437 }
438 
439 static inline void
_math_closure(hb_subset_plan_t * plan,hb_set_t * glyphset)440 _math_closure (hb_subset_plan_t *plan,
441                hb_set_t         *glyphset)
442 {
443   hb_blob_ptr_t<OT::MATH> math = plan->source_table<OT::MATH> ();
444   if (math->has_data ())
445     math->closure_glyphs (glyphset);
446   math.destroy ();
447 }
448 
449 
450 static inline void
_remove_invalid_gids(hb_set_t * glyphs,unsigned int num_glyphs)451 _remove_invalid_gids (hb_set_t *glyphs,
452 		      unsigned int num_glyphs)
453 {
454   glyphs->del_range (num_glyphs, HB_SET_VALUE_INVALID);
455 }
456 
457 static void
_populate_unicodes_to_retain(const hb_set_t * unicodes,const hb_set_t * glyphs,hb_subset_plan_t * plan)458 _populate_unicodes_to_retain (const hb_set_t *unicodes,
459                               const hb_set_t *glyphs,
460                               hb_subset_plan_t *plan)
461 {
462   OT::cmap::accelerator_t cmap (plan->source);
463   unsigned size_threshold = plan->source->get_num_glyphs ();
464   if (glyphs->is_empty () && unicodes->get_population () < size_threshold)
465   {
466 
467     const hb_map_t* unicode_to_gid = nullptr;
468     if (plan->accelerator)
469       unicode_to_gid = &plan->accelerator->unicode_to_gid;
470 
471     // This is approach to collection is faster, but can only be used  if glyphs
472     // are not being explicitly added to the subset and the input unicodes set is
473     // not excessively large (eg. an inverted set).
474     plan->unicode_to_new_gid_list.alloc (unicodes->get_population ());
475     if (!unicode_to_gid) {
476       for (hb_codepoint_t cp : *unicodes)
477       {
478         hb_codepoint_t gid;
479         if (!cmap.get_nominal_glyph (cp, &gid))
480         {
481           DEBUG_MSG(SUBSET, nullptr, "Drop U+%04X; no gid", cp);
482           continue;
483         }
484 
485         plan->codepoint_to_glyph->set (cp, gid);
486         plan->unicode_to_new_gid_list.push (hb_pair (cp, gid));
487       }
488     } else {
489       // Use in memory unicode to gid map it's faster then looking up from
490       // the map. This code is mostly duplicated from above to avoid doing
491       // conditionals on the presence of the unicode_to_gid map each
492       // iteration.
493       for (hb_codepoint_t cp : *unicodes)
494       {
495         hb_codepoint_t gid = unicode_to_gid->get (cp);
496         if (gid == HB_MAP_VALUE_INVALID)
497         {
498           DEBUG_MSG(SUBSET, nullptr, "Drop U+%04X; no gid", cp);
499           continue;
500         }
501 
502         plan->codepoint_to_glyph->set (cp, gid);
503         plan->unicode_to_new_gid_list.push (hb_pair (cp, gid));
504       }
505     }
506   }
507   else
508   {
509     // This approach is slower, but can handle adding in glyphs to the subset and will match
510     // them with cmap entries.
511 
512     hb_map_t unicode_glyphid_map_storage;
513     hb_set_t cmap_unicodes_storage;
514     const hb_map_t* unicode_glyphid_map = &unicode_glyphid_map_storage;
515     const hb_set_t* cmap_unicodes = &cmap_unicodes_storage;
516 
517     if (!plan->accelerator) {
518       cmap.collect_mapping (&cmap_unicodes_storage, &unicode_glyphid_map_storage);
519       plan->unicode_to_new_gid_list.alloc (hb_min(unicodes->get_population ()
520                                                   + glyphs->get_population (),
521                                                   cmap_unicodes->get_population ()));
522     } else {
523       unicode_glyphid_map = &plan->accelerator->unicode_to_gid;
524       cmap_unicodes = &plan->accelerator->unicodes;
525     }
526 
527     if (plan->accelerator &&
528 	unicodes->get_population () < cmap_unicodes->get_population () &&
529 	glyphs->get_population () < cmap_unicodes->get_population ())
530     {
531       auto &gid_to_unicodes = plan->accelerator->gid_to_unicodes;
532       for (hb_codepoint_t gid : *glyphs)
533       {
534         auto unicodes = gid_to_unicodes.get (gid);
535 
536 	for (hb_codepoint_t cp : unicodes)
537 	{
538 	  plan->codepoint_to_glyph->set (cp, gid);
539 	  plan->unicode_to_new_gid_list.push (hb_pair (cp, gid));
540 	}
541       }
542       for (hb_codepoint_t cp : *unicodes)
543       {
544 	/* Don't double-add entry. */
545 	if (plan->codepoint_to_glyph->has (cp))
546 	  continue;
547 
548 	hb_codepoint_t gid = (*unicode_glyphid_map)[cp];
549 	plan->codepoint_to_glyph->set (cp, gid);
550 	plan->unicode_to_new_gid_list.push (hb_pair (cp, gid));
551       }
552       plan->unicode_to_new_gid_list.qsort ();
553     }
554     else
555     {
556       for (hb_codepoint_t cp : *cmap_unicodes)
557       {
558 	hb_codepoint_t gid = (*unicode_glyphid_map)[cp];
559 	if (!unicodes->has (cp) && !glyphs->has (gid))
560 	  continue;
561 
562 	plan->codepoint_to_glyph->set (cp, gid);
563 	plan->unicode_to_new_gid_list.push (hb_pair (cp, gid));
564       }
565     }
566 
567     /* Add gids which where requested, but not mapped in cmap */
568     for (hb_codepoint_t gid : *glyphs)
569     {
570       if (gid >= plan->source->get_num_glyphs ())
571 	break;
572       plan->_glyphset_gsub->add (gid);
573     }
574   }
575 
576   auto &arr = plan->unicode_to_new_gid_list;
577   if (arr.length)
578   {
579     plan->unicodes->add_sorted_array (&arr.arrayZ->first, arr.length, sizeof (*arr.arrayZ));
580     plan->_glyphset_gsub->add_array (&arr.arrayZ->second, arr.length, sizeof (*arr.arrayZ));
581   }
582 }
583 
584 #ifndef HB_COMPOSITE_OPERATIONS_PER_GLYPH
585 #define HB_COMPOSITE_OPERATIONS_PER_GLYPH 64
586 #endif
587 
588 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)589 _glyf_add_gid_and_children (const OT::glyf_accelerator_t &glyf,
590 			    hb_codepoint_t gid,
591 			    hb_set_t *gids_to_retain,
592 			    int operation_count,
593 			    unsigned depth = 0)
594 {
595   if (unlikely (depth++ > HB_MAX_NESTING_LEVEL)) return operation_count;
596   if (unlikely (--operation_count < 0)) return operation_count;
597   /* Check if is already visited */
598   if (gids_to_retain->has (gid)) return operation_count;
599 
600   gids_to_retain->add (gid);
601 
602   for (auto item : glyf.glyph_for_gid (gid).get_composite_iterator ())
603     operation_count =
604       _glyf_add_gid_and_children (glyf,
605 				  item.get_gid (),
606 				  gids_to_retain,
607 				  operation_count,
608 				  depth);
609 
610   return operation_count;
611 }
612 
613 static void
_populate_gids_to_retain(hb_subset_plan_t * plan,hb_set_t * drop_tables)614 _populate_gids_to_retain (hb_subset_plan_t* plan,
615 		          hb_set_t* drop_tables)
616 {
617   OT::glyf_accelerator_t glyf (plan->source);
618 #ifndef HB_NO_SUBSET_CFF
619   OT::cff1::accelerator_t cff (plan->source);
620 #endif
621 
622   plan->_glyphset_gsub->add (0); // Not-def
623 
624   _cmap_closure (plan->source, plan->unicodes, plan->_glyphset_gsub);
625 
626 #ifndef HB_NO_SUBSET_LAYOUT
627   if (!drop_tables->has (HB_OT_TAG_GSUB))
628     // closure all glyphs/lookups/features needed for GSUB substitutions.
629     _closure_glyphs_lookups_features<GSUB> (
630         plan,
631         plan->_glyphset_gsub,
632         plan->gsub_lookups,
633         plan->gsub_features,
634         plan->gsub_langsys,
635         plan->gsub_feature_record_cond_idx_map,
636         plan->gsub_feature_substitutes_map);
637 
638   if (!drop_tables->has (HB_OT_TAG_GPOS))
639     _closure_glyphs_lookups_features<GPOS> (
640         plan,
641         plan->_glyphset_gsub,
642         plan->gpos_lookups,
643         plan->gpos_features,
644         plan->gpos_langsys,
645         plan->gpos_feature_record_cond_idx_map,
646         plan->gpos_feature_substitutes_map);
647 #endif
648   _remove_invalid_gids (plan->_glyphset_gsub, plan->source->get_num_glyphs ());
649 
650   hb_set_set (plan->_glyphset_mathed, plan->_glyphset_gsub);
651   if (!drop_tables->has (HB_OT_TAG_MATH))
652   {
653     _math_closure (plan, plan->_glyphset_mathed);
654     _remove_invalid_gids (plan->_glyphset_mathed, plan->source->get_num_glyphs ());
655   }
656 
657   hb_set_t cur_glyphset = *plan->_glyphset_mathed;
658   if (!drop_tables->has (HB_OT_TAG_COLR))
659   {
660     _colr_closure (plan->source, plan->colrv1_layers, plan->colr_palettes, &cur_glyphset);
661     _remove_invalid_gids (&cur_glyphset, plan->source->get_num_glyphs ());
662   }
663 
664   hb_set_set (plan->_glyphset_colred, &cur_glyphset);
665 
666   /* Populate a full set of glyphs to retain by adding all referenced
667    * composite glyphs. */
668   if (glyf.has_data ())
669     for (hb_codepoint_t gid : cur_glyphset)
670       _glyf_add_gid_and_children (glyf, gid, plan->_glyphset,
671 				  cur_glyphset.get_population () * HB_COMPOSITE_OPERATIONS_PER_GLYPH);
672   else
673     plan->_glyphset->union_ (cur_glyphset);
674 #ifndef HB_NO_SUBSET_CFF
675   if (!plan->accelerator || plan->accelerator->has_seac)
676   {
677     bool has_seac = false;
678     if (cff.is_valid ())
679       for (hb_codepoint_t gid : cur_glyphset)
680 	if (_add_cff_seac_components (cff, gid, plan->_glyphset))
681 	  has_seac = true;
682     plan->has_seac = has_seac;
683   }
684 #endif
685 
686   _remove_invalid_gids (plan->_glyphset, plan->source->get_num_glyphs ());
687 
688 
689 #ifndef HB_NO_VAR
690   if (!drop_tables->has (HB_OT_TAG_GDEF))
691     _collect_layout_variation_indices (plan);
692 #endif
693 }
694 
695 static void
_create_glyph_map_gsub(const hb_set_t * glyph_set_gsub,const hb_map_t * glyph_map,hb_map_t * out)696 _create_glyph_map_gsub (const hb_set_t* glyph_set_gsub,
697                         const hb_map_t* glyph_map,
698                         hb_map_t* out)
699 {
700   + hb_iter (glyph_set_gsub)
701   | hb_map ([&] (hb_codepoint_t gid) {
702     return hb_pair_t<hb_codepoint_t, hb_codepoint_t> (gid,
703                                                       glyph_map->get (gid));
704   })
705   | hb_sink (out)
706   ;
707 }
708 
709 static void
_create_old_gid_to_new_gid_map(const hb_face_t * face,bool retain_gids,const hb_set_t * all_gids_to_retain,hb_map_t * glyph_map,hb_map_t * reverse_glyph_map,unsigned int * num_glyphs)710 _create_old_gid_to_new_gid_map (const hb_face_t *face,
711 				bool		 retain_gids,
712 				const hb_set_t	*all_gids_to_retain,
713 				hb_map_t	*glyph_map, /* OUT */
714 				hb_map_t	*reverse_glyph_map, /* OUT */
715 				unsigned int	*num_glyphs /* OUT */)
716 {
717   unsigned pop = all_gids_to_retain->get_population ();
718   reverse_glyph_map->resize (pop);
719   glyph_map->resize (pop);
720 
721   if (!retain_gids)
722   {
723     + hb_enumerate (hb_iter (all_gids_to_retain), (hb_codepoint_t) 0)
724     | hb_sink (reverse_glyph_map)
725     ;
726     *num_glyphs = reverse_glyph_map->get_population ();
727   }
728   else
729   {
730     + hb_iter (all_gids_to_retain)
731     | hb_map ([] (hb_codepoint_t _) {
732 		return hb_pair_t<hb_codepoint_t, hb_codepoint_t> (_, _);
733 	      })
734     | hb_sink (reverse_glyph_map)
735     ;
736 
737     hb_codepoint_t max_glyph = HB_SET_VALUE_INVALID;
738     hb_set_previous (all_gids_to_retain, &max_glyph);
739 
740     *num_glyphs = max_glyph + 1;
741   }
742 
743   + reverse_glyph_map->iter ()
744   | hb_map (&hb_pair_t<hb_codepoint_t, hb_codepoint_t>::reverse)
745   | hb_sink (glyph_map)
746   ;
747 }
748 
749 static void
_nameid_closure(hb_face_t * face,hb_set_t * nameids,bool all_axes_pinned,hb_hashmap_t<hb_tag_t,float> * user_axes_location)750 _nameid_closure (hb_face_t *face,
751 		 hb_set_t  *nameids,
752 		 bool all_axes_pinned,
753 		 hb_hashmap_t<hb_tag_t, float> *user_axes_location)
754 {
755 #ifndef HB_NO_STYLE
756   face->table.STAT->collect_name_ids (user_axes_location, nameids);
757 #endif
758 #ifndef HB_NO_VAR
759   if (!all_axes_pinned)
760     face->table.fvar->collect_name_ids (user_axes_location, nameids);
761 #endif
762 }
763 
764 #ifndef HB_NO_VAR
765 static void
_normalize_axes_location(hb_face_t * face,hb_subset_plan_t * plan)766 _normalize_axes_location (hb_face_t *face, hb_subset_plan_t *plan)
767 {
768   if (plan->user_axes_location->is_empty ())
769     return;
770 
771   hb_array_t<const OT::AxisRecord> axes = face->table.fvar->get_axes ();
772 
773   bool has_avar = face->table.avar->has_data ();
774   const OT::SegmentMaps *seg_maps = nullptr;
775   if (has_avar)
776     seg_maps = face->table.avar->get_segment_maps ();
777 
778   bool axis_not_pinned = false;
779   unsigned old_axis_idx = 0, new_axis_idx = 0;
780   for (const auto& axis : axes)
781   {
782     hb_tag_t axis_tag = axis.get_axis_tag ();
783     plan->axes_old_index_tag_map->set (old_axis_idx, axis_tag);
784 
785     if (!plan->user_axes_location->has (axis_tag))
786     {
787       axis_not_pinned = true;
788       plan->axes_index_map->set (old_axis_idx, new_axis_idx);
789       new_axis_idx++;
790     }
791     else
792     {
793       int normalized_v = axis.normalize_axis_value (plan->user_axes_location->get (axis_tag));
794       if (has_avar && old_axis_idx < face->table.avar->get_axis_count ())
795       {
796         normalized_v = seg_maps->map (normalized_v);
797       }
798       plan->axes_location->set (axis_tag, normalized_v);
799       if (normalized_v != 0)
800         plan->pinned_at_default = false;
801     }
802     if (has_avar)
803       seg_maps = &StructAfter<OT::SegmentMaps> (*seg_maps);
804 
805     old_axis_idx++;
806   }
807   plan->all_axes_pinned = !axis_not_pinned;
808 }
809 #endif
810 /**
811  * hb_subset_plan_create_or_fail:
812  * @face: font face to create the plan for.
813  * @input: a #hb_subset_input_t input.
814  *
815  * Computes a plan for subsetting the supplied face according
816  * to a provided input. The plan describes
817  * which tables and glyphs should be retained.
818  *
819  * Return value: (transfer full): New subset plan. Destroy with
820  * hb_subset_plan_destroy(). If there is a failure creating the plan
821  * nullptr will be returned.
822  *
823  * Since: 4.0.0
824  **/
825 hb_subset_plan_t *
hb_subset_plan_create_or_fail(hb_face_t * face,const hb_subset_input_t * input)826 hb_subset_plan_create_or_fail (hb_face_t	 *face,
827                                const hb_subset_input_t *input)
828 {
829   hb_subset_plan_t *plan;
830   if (unlikely (!(plan = hb_object_create<hb_subset_plan_t> ())))
831     return nullptr;
832 
833   plan->successful = true;
834   plan->flags = input->flags;
835   plan->unicodes = hb_set_create ();
836 
837   plan->unicode_to_new_gid_list.init ();
838 
839   plan->name_ids = hb_set_copy (input->sets.name_ids);
840   plan->name_languages = hb_set_copy (input->sets.name_languages);
841   plan->layout_features = hb_set_copy (input->sets.layout_features);
842   plan->layout_scripts = hb_set_copy (input->sets.layout_scripts);
843   plan->glyphs_requested = hb_set_copy (input->sets.glyphs);
844   plan->drop_tables = hb_set_copy (input->sets.drop_tables);
845   plan->no_subset_tables = hb_set_copy (input->sets.no_subset_tables);
846   plan->source = hb_face_reference (face);
847   plan->dest = hb_face_builder_create ();
848 
849   plan->_glyphset = hb_set_create ();
850   plan->_glyphset_gsub = hb_set_create ();
851   plan->_glyphset_mathed = hb_set_create ();
852   plan->_glyphset_colred = hb_set_create ();
853   plan->codepoint_to_glyph = hb_map_create ();
854   plan->glyph_map = hb_map_create ();
855   plan->reverse_glyph_map = hb_map_create ();
856   plan->glyph_map_gsub = hb_map_create ();
857   plan->gsub_lookups = hb_map_create ();
858   plan->gpos_lookups = hb_map_create ();
859 
860   plan->check_success (plan->gsub_langsys = hb_hashmap_create<unsigned, hb::unique_ptr<hb_set_t>> ());
861   plan->check_success (plan->gpos_langsys = hb_hashmap_create<unsigned, hb::unique_ptr<hb_set_t>> ());
862 
863   plan->gsub_features = hb_map_create ();
864   plan->gpos_features = hb_map_create ();
865 
866   plan->check_success (plan->gsub_feature_record_cond_idx_map = hb_hashmap_create<unsigned, hb::shared_ptr<hb_set_t>> ());
867   plan->check_success (plan->gpos_feature_record_cond_idx_map = hb_hashmap_create<unsigned, hb::shared_ptr<hb_set_t>> ());
868 
869   plan->check_success (plan->gsub_feature_substitutes_map = hb_hashmap_create<unsigned, const OT::Feature*> ());
870   plan->check_success (plan->gpos_feature_substitutes_map = hb_hashmap_create<unsigned, const OT::Feature*> ());
871 
872   plan->colrv1_layers = hb_map_create ();
873   plan->colr_palettes = hb_map_create ();
874   plan->check_success (plan->layout_variation_idx_delta_map = hb_hashmap_create<unsigned, hb_pair_t<unsigned, int>> ());
875   plan->gdef_varstore_inner_maps.init ();
876 
877   plan->check_success (plan->sanitized_table_cache = hb_hashmap_create<hb_tag_t, hb::unique_ptr<hb_blob_t>> ());
878   plan->check_success (plan->axes_location = hb_hashmap_create<hb_tag_t, int> ());
879   plan->check_success (plan->user_axes_location = hb_hashmap_create<hb_tag_t, float> ());
880   if (plan->user_axes_location && input->axes_location)
881       *plan->user_axes_location = *input->axes_location;
882   plan->check_success (plan->axes_index_map = hb_map_create ());
883   plan->check_success (plan->axes_old_index_tag_map = hb_map_create ());
884   plan->all_axes_pinned = false;
885   plan->pinned_at_default = true;
886 
887   plan->check_success (plan->vmtx_map = hb_hashmap_create<unsigned, hb_pair_t<unsigned, int>> ());
888   plan->check_success (plan->hmtx_map = hb_hashmap_create<unsigned, hb_pair_t<unsigned, int>> ());
889 
890 #ifdef HB_EXPERIMENTAL_API
891   plan->check_success (plan->name_table_overrides = hb_hashmap_create<hb_ot_name_record_ids_t, hb_bytes_t> ());
892   if (plan->name_table_overrides && input->name_table_overrides)
893   {
894     for (auto _ : *input->name_table_overrides)
895     {
896       hb_bytes_t name_bytes = _.second;
897       unsigned len = name_bytes.length;
898       char *name_str = (char *) hb_malloc (len);
899       if (unlikely (!plan->check_success (name_str)))
900         break;
901 
902       hb_memcpy (name_str, name_bytes.arrayZ, len);
903       plan->name_table_overrides->set (_.first, hb_bytes_t (name_str, len));
904     }
905   }
906 #endif
907 
908   void* accel = hb_face_get_user_data(face, hb_subset_accelerator_t::user_data_key());
909 
910   plan->attach_accelerator_data = input->attach_accelerator_data;
911   plan->force_long_loca = input->force_long_loca;
912   if (accel)
913     plan->accelerator = (hb_subset_accelerator_t*) accel;
914 
915 
916   if (unlikely (plan->in_error ())) {
917     hb_subset_plan_destroy (plan);
918     return nullptr;
919   }
920 
921 #ifndef HB_NO_VAR
922   _normalize_axes_location (face, plan);
923 #endif
924 
925   _populate_unicodes_to_retain (input->sets.unicodes, input->sets.glyphs, plan);
926 
927   _populate_gids_to_retain (plan, input->sets.drop_tables);
928 
929   _create_old_gid_to_new_gid_map (face,
930                                   input->flags & HB_SUBSET_FLAGS_RETAIN_GIDS,
931 				  plan->_glyphset,
932 				  plan->glyph_map,
933 				  plan->reverse_glyph_map,
934 				  &plan->_num_output_glyphs);
935 
936   _create_glyph_map_gsub (
937       plan->_glyphset_gsub,
938       plan->glyph_map,
939       plan->glyph_map_gsub);
940 
941   // Now that we have old to new gid map update the unicode to new gid list.
942   for (unsigned i = 0; i < plan->unicode_to_new_gid_list.length; i++)
943   {
944     // Use raw array access for performance.
945     plan->unicode_to_new_gid_list.arrayZ[i].second =
946         plan->glyph_map->get(plan->unicode_to_new_gid_list.arrayZ[i].second);
947   }
948 
949   _nameid_closure (face, plan->name_ids, plan->all_axes_pinned, plan->user_axes_location);
950   if (unlikely (plan->in_error ())) {
951     hb_subset_plan_destroy (plan);
952     return nullptr;
953   }
954 
955 
956   if (plan->attach_accelerator_data)
957   {
958     hb_multimap_t gid_to_unicodes;
959 
960     hb_map_t &unicode_to_gid = *plan->codepoint_to_glyph;
961 
962     for (auto unicode : *plan->unicodes)
963     {
964       auto gid = unicode_to_gid[unicode];
965       gid_to_unicodes.add (gid, unicode);
966     }
967 
968     plan->inprogress_accelerator =
969       hb_subset_accelerator_t::create (*plan->codepoint_to_glyph,
970 				       gid_to_unicodes,
971                                        *plan->unicodes,
972 				       plan->has_seac);
973   }
974 
975 
976   return plan;
977 }
978 
979 /**
980  * hb_subset_plan_destroy:
981  * @plan: a #hb_subset_plan_t
982  *
983  * Decreases the reference count on @plan, and if it reaches zero, destroys
984  * @plan, freeing all memory.
985  *
986  * Since: 4.0.0
987  **/
988 void
hb_subset_plan_destroy(hb_subset_plan_t * plan)989 hb_subset_plan_destroy (hb_subset_plan_t *plan)
990 {
991   if (!hb_object_destroy (plan)) return;
992 
993   hb_free (plan);
994 }
995 
996 /**
997  * hb_subset_plan_old_to_new_glyph_mapping:
998  * @plan: a subsetting plan.
999  *
1000  * Returns the mapping between glyphs in the original font to glyphs in the
1001  * subset that will be produced by @plan
1002  *
1003  * Return value: (transfer none):
1004  * A pointer to the #hb_map_t of the mapping.
1005  *
1006  * Since: 4.0.0
1007  **/
1008 const hb_map_t*
hb_subset_plan_old_to_new_glyph_mapping(const hb_subset_plan_t * plan)1009 hb_subset_plan_old_to_new_glyph_mapping (const hb_subset_plan_t *plan)
1010 {
1011   return plan->glyph_map;
1012 }
1013 
1014 /**
1015  * hb_subset_plan_new_to_old_glyph_mapping:
1016  * @plan: a subsetting plan.
1017  *
1018  * Returns the mapping between glyphs in the subset that will be produced by
1019  * @plan and the glyph in the original font.
1020  *
1021  * Return value: (transfer none):
1022  * A pointer to the #hb_map_t of the mapping.
1023  *
1024  * Since: 4.0.0
1025  **/
1026 const hb_map_t*
hb_subset_plan_new_to_old_glyph_mapping(const hb_subset_plan_t * plan)1027 hb_subset_plan_new_to_old_glyph_mapping (const hb_subset_plan_t *plan)
1028 {
1029   return plan->reverse_glyph_map;
1030 }
1031 
1032 /**
1033  * hb_subset_plan_unicode_to_old_glyph_mapping:
1034  * @plan: a subsetting plan.
1035  *
1036  * Returns the mapping between codepoints in the original font and the
1037  * associated glyph id in the original font.
1038  *
1039  * Return value: (transfer none):
1040  * A pointer to the #hb_map_t of the mapping.
1041  *
1042  * Since: 4.0.0
1043  **/
1044 const hb_map_t*
hb_subset_plan_unicode_to_old_glyph_mapping(const hb_subset_plan_t * plan)1045 hb_subset_plan_unicode_to_old_glyph_mapping (const hb_subset_plan_t *plan)
1046 {
1047   return plan->codepoint_to_glyph;
1048 }
1049 
1050 /**
1051  * hb_subset_plan_reference: (skip)
1052  * @plan: a #hb_subset_plan_t object.
1053  *
1054  * Increases the reference count on @plan.
1055  *
1056  * Return value: @plan.
1057  *
1058  * Since: 4.0.0
1059  **/
1060 hb_subset_plan_t *
hb_subset_plan_reference(hb_subset_plan_t * plan)1061 hb_subset_plan_reference (hb_subset_plan_t *plan)
1062 {
1063   return hb_object_reference (plan);
1064 }
1065 
1066 /**
1067  * hb_subset_plan_set_user_data: (skip)
1068  * @plan: a #hb_subset_plan_t object.
1069  * @key: The user-data key to set
1070  * @data: A pointer to the user data
1071  * @destroy: (nullable): A callback to call when @data is not needed anymore
1072  * @replace: Whether to replace an existing data with the same key
1073  *
1074  * Attaches a user-data key/data pair to the given subset plan object.
1075  *
1076  * Return value: `true` if success, `false` otherwise
1077  *
1078  * Since: 4.0.0
1079  **/
1080 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)1081 hb_subset_plan_set_user_data (hb_subset_plan_t   *plan,
1082                               hb_user_data_key_t *key,
1083                               void               *data,
1084                               hb_destroy_func_t   destroy,
1085                               hb_bool_t	          replace)
1086 {
1087   return hb_object_set_user_data (plan, key, data, destroy, replace);
1088 }
1089 
1090 /**
1091  * hb_subset_plan_get_user_data: (skip)
1092  * @plan: a #hb_subset_plan_t object.
1093  * @key: The user-data key to query
1094  *
1095  * Fetches the user data associated with the specified key,
1096  * attached to the specified subset plan object.
1097  *
1098  * Return value: (transfer none): A pointer to the user data
1099  *
1100  * Since: 4.0.0
1101  **/
1102 void *
hb_subset_plan_get_user_data(const hb_subset_plan_t * plan,hb_user_data_key_t * key)1103 hb_subset_plan_get_user_data (const hb_subset_plan_t *plan,
1104                               hb_user_data_key_t     *key)
1105 {
1106   return hb_object_get_user_data (plan, key);
1107 }
1108