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