2 * Copyright © 2018 Google, Inc.
4 * This is part of HarfBuzz, a text shaping library.
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.
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
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.
24 * Google Author(s): Garret Rieger, Roderick Sheeter
27 #include "hb-subset-plan.hh"
28 #include "hb-subset-accelerator.hh"
30 #include "hb-multimap.hh"
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-cff2-table.hh"
40 #include "OT/Color/COLR/COLR.hh"
41 #include "OT/Color/COLR/colrv1-closure.hh"
42 #include "OT/Color/CPAL/CPAL.hh"
43 #include "hb-ot-var-fvar-table.hh"
44 #include "hb-ot-var-avar-table.hh"
45 #include "hb-ot-stat-table.hh"
46 #include "hb-ot-math-table.hh"
48 using OT::Layout::GSUB
;
49 using OT::Layout::GPOS
;
52 hb_subset_accelerator_t::~hb_subset_accelerator_t ()
54 if (cmap_cache
&& destroy_cmap_cache
)
55 destroy_cmap_cache ((void*) cmap_cache
);
57 #ifndef HB_NO_SUBSET_CFF
61 hb_face_destroy (source
);
65 typedef hb_hashmap_t
<unsigned, hb::unique_ptr
<hb_set_t
>> script_langsys_map
;
66 #ifndef HB_NO_SUBSET_CFF
68 _add_cff_seac_components (const OT::cff1::accelerator_subset_t
&cff
,
70 hb_set_t
*gids_to_retain
)
72 hb_codepoint_t base_gid
, accent_gid
;
73 if (cff
.get_seac_components (gid
, &base_gid
, &accent_gid
))
75 gids_to_retain
->add (base_gid
);
76 gids_to_retain
->add (accent_gid
);
84 _remap_palette_indexes (const hb_set_t
*palette_indexes
,
85 hb_map_t
*mapping
/* OUT */)
88 for (unsigned palette_index
: palette_indexes
->iter ())
90 if (palette_index
== 0xFFFF)
92 mapping
->set (palette_index
, palette_index
);
95 mapping
->set (palette_index
, new_idx
);
101 _remap_indexes (const hb_set_t
*indexes
,
102 hb_map_t
*mapping
/* OUT */)
104 for (auto _
: + hb_enumerate (indexes
->iter ()))
105 mapping
->set (_
.second
, _
.first
);
109 #ifndef HB_NO_SUBSET_LAYOUT
112 * Removes all tags from 'tags' that are not in filter. Additionally eliminates any duplicates.
113 * Returns true if anything was removed (not including duplicates).
115 static bool _filter_tag_list(hb_vector_t
<hb_tag_t
>* tags
, /* IN/OUT */
116 const hb_set_t
* filter
)
118 hb_vector_t
<hb_tag_t
> out
;
119 out
.alloc (tags
->get_size() + 1); // +1 is to allocate room for the null terminator.
121 bool removed
= false;
124 for (hb_tag_t tag
: *tags
)
127 if (visited
.has (tag
)) continue;
129 if (!filter
->has (tag
))
139 // The collect function needs a null element to signal end of the array.
140 out
.push (HB_TAG_NONE
);
142 hb_swap (out
, *tags
);
146 template <typename T
>
147 static void _collect_layout_indices (hb_subset_plan_t
*plan
,
149 hb_set_t
*lookup_indices
, /* OUT */
150 hb_set_t
*feature_indices
, /* OUT */
151 hb_hashmap_t
<unsigned, hb::shared_ptr
<hb_set_t
>> *feature_record_cond_idx_map
, /* OUT */
152 hb_hashmap_t
<unsigned, const OT::Feature
*> *feature_substitutes_map
, /* OUT */
153 hb_set_t
& catch_all_record_feature_idxes
, /* OUT */
154 hb_hashmap_t
<unsigned, hb_pair_t
<const void*, const void*>>& catch_all_record_idx_feature_map
/* OUT */)
156 unsigned num_features
= table
.get_feature_count ();
157 hb_vector_t
<hb_tag_t
> features
;
158 if (!plan
->check_success (features
.resize (num_features
))) return;
159 table
.get_feature_tags (0, &num_features
, features
.arrayZ
);
160 bool retain_all_features
= !_filter_tag_list (&features
, &plan
->layout_features
);
162 unsigned num_scripts
= table
.get_script_count ();
163 hb_vector_t
<hb_tag_t
> scripts
;
164 if (!plan
->check_success (scripts
.resize (num_scripts
))) return;
165 table
.get_script_tags (0, &num_scripts
, scripts
.arrayZ
);
166 bool retain_all_scripts
= !_filter_tag_list (&scripts
, &plan
->layout_scripts
);
168 if (!plan
->check_success (!features
.in_error ()) || !features
169 || !plan
->check_success (!scripts
.in_error ()) || !scripts
)
172 hb_ot_layout_collect_features (plan
->source
,
174 retain_all_scripts
? nullptr : scripts
.arrayZ
,
176 retain_all_features
? nullptr : features
.arrayZ
,
180 // collect feature substitutes with variations
181 if (!plan
->user_axes_location
.is_empty ())
183 hb_hashmap_t
<hb::shared_ptr
<hb_map_t
>, unsigned> conditionset_map
;
184 OT::hb_collect_feature_substitutes_with_var_context_t c
=
186 &plan
->axes_old_index_tag_map
,
187 &plan
->axes_location
,
188 feature_record_cond_idx_map
,
189 feature_substitutes_map
,
190 catch_all_record_feature_idxes
,
198 table
.collect_feature_substitutes_with_variations (&c
);
202 for (unsigned feature_index
: *feature_indices
)
204 const OT::Feature
* f
= &(table
.get_feature (feature_index
));
205 const OT::Feature
**p
= nullptr;
206 if (feature_substitutes_map
->has (feature_index
, &p
))
209 f
->add_lookup_indexes_to (lookup_indices
);
213 if (catch_all_record_feature_idxes
)
215 for (unsigned feature_index
: catch_all_record_feature_idxes
)
217 const OT::Feature
& f
= table
.get_feature (feature_index
);
218 f
.add_lookup_indexes_to (lookup_indices
);
219 const void *tag
= reinterpret_cast<const void*> (&(table
.get_feature_list ().get_tag (feature_index
)));
220 catch_all_record_idx_feature_map
.set (feature_index
, hb_pair (&f
, tag
));
224 // If all axes are pinned then all feature variations will be dropped so there's no need
225 // to collect lookups from them.
226 if (!plan
->all_axes_pinned
)
227 table
.feature_variation_collect_lookups (feature_indices
,
228 plan
->user_axes_location
.is_empty () ? nullptr: feature_record_cond_idx_map
,
235 _GSUBGPOS_find_duplicate_features (const OT::GSUBGPOS
&g
,
236 const hb_map_t
*lookup_indices
,
237 const hb_set_t
*feature_indices
,
238 const hb_hashmap_t
<unsigned, const OT::Feature
*> *feature_substitutes_map
,
239 hb_map_t
*duplicate_feature_map
/* OUT */)
241 if (feature_indices
->is_empty ()) return;
242 hb_hashmap_t
<hb_tag_t
, hb::unique_ptr
<hb_set_t
>> unique_features
;
243 //find out duplicate features after subset
244 for (unsigned i
: feature_indices
->iter ())
246 hb_tag_t t
= g
.get_feature_tag (i
);
247 if (t
== HB_MAP_VALUE_INVALID
) continue;
248 if (!unique_features
.has (t
))
250 if (unlikely (!unique_features
.set (t
, hb::unique_ptr
<hb_set_t
> {hb_set_create ()})))
252 if (unique_features
.has (t
))
253 unique_features
.get (t
)->add (i
);
254 duplicate_feature_map
->set (i
, i
);
260 hb_set_t
* same_tag_features
= unique_features
.get (t
);
261 for (unsigned other_f_index
: same_tag_features
->iter ())
263 const OT::Feature
* f
= &(g
.get_feature (i
));
264 const OT::Feature
**p
= nullptr;
265 if (feature_substitutes_map
->has (i
, &p
))
268 const OT::Feature
* other_f
= &(g
.get_feature (other_f_index
));
269 if (feature_substitutes_map
->has (other_f_index
, &p
))
273 + hb_iter (f
->lookupIndex
)
274 | hb_filter (lookup_indices
)
278 + hb_iter (other_f
->lookupIndex
)
279 | hb_filter (lookup_indices
)
282 bool is_equal
= true;
283 for (; f_iter
&& other_f_iter
; f_iter
++, other_f_iter
++)
285 unsigned a
= *f_iter
;
286 unsigned b
= *other_f_iter
;
287 if (a
!= b
) { is_equal
= false; break; }
290 if (is_equal
== false || f_iter
|| other_f_iter
) continue;
293 duplicate_feature_map
->set (i
, other_f_index
);
299 same_tag_features
->add (i
);
300 duplicate_feature_map
->set (i
, i
);
305 template <typename T
>
307 _closure_glyphs_lookups_features (hb_subset_plan_t
*plan
,
308 hb_set_t
*gids_to_retain
,
311 script_langsys_map
*langsys_map
,
312 hb_hashmap_t
<unsigned, hb::shared_ptr
<hb_set_t
>> *feature_record_cond_idx_map
,
313 hb_hashmap_t
<unsigned, const OT::Feature
*> *feature_substitutes_map
,
314 hb_set_t
&catch_all_record_feature_idxes
,
315 hb_hashmap_t
<unsigned, hb_pair_t
<const void*, const void*>>& catch_all_record_idx_feature_map
)
317 hb_blob_ptr_t
<T
> table
= plan
->source_table
<T
> ();
318 hb_tag_t table_tag
= table
->tableTag
;
319 hb_set_t lookup_indices
, feature_indices
;
320 _collect_layout_indices
<T
> (plan
,
324 feature_record_cond_idx_map
,
325 feature_substitutes_map
,
326 catch_all_record_feature_idxes
,
327 catch_all_record_idx_feature_map
);
329 if (table_tag
== HB_OT_TAG_GSUB
&& !(plan
->flags
& HB_SUBSET_FLAGS_NO_LAYOUT_CLOSURE
))
330 hb_ot_layout_lookups_substitute_closure (plan
->source
,
333 table
->closure_lookups (plan
->source
,
336 _remap_indexes (&lookup_indices
, lookups
);
339 table
->prune_features (lookups
,
340 plan
->user_axes_location
.is_empty () ? nullptr : feature_record_cond_idx_map
,
341 feature_substitutes_map
,
343 hb_map_t duplicate_feature_map
;
344 _GSUBGPOS_find_duplicate_features (*table
, lookups
, &feature_indices
, feature_substitutes_map
, &duplicate_feature_map
);
346 feature_indices
.clear ();
347 table
->prune_langsys (&duplicate_feature_map
, &plan
->layout_scripts
, langsys_map
, &feature_indices
);
348 _remap_indexes (&feature_indices
, features
);
357 _generate_varstore_inner_maps (const hb_set_t
& varidx_set
,
358 unsigned subtable_count
,
359 hb_vector_t
<hb_inc_bimap_t
> &inner_maps
/* OUT */)
361 if (varidx_set
.is_empty () || subtable_count
== 0) return;
363 if (unlikely (!inner_maps
.resize (subtable_count
))) return;
364 for (unsigned idx
: varidx_set
)
366 uint16_t major
= idx
>> 16;
367 uint16_t minor
= idx
& 0xFFFF;
369 if (major
>= subtable_count
)
371 inner_maps
[major
].add (minor
);
375 static inline hb_font_t
*
376 _get_hb_font_with_variations (const hb_subset_plan_t
*plan
)
378 hb_font_t
*font
= hb_font_create (plan
->source
);
380 hb_vector_t
<hb_variation_t
> vars
;
381 if (!vars
.alloc (plan
->user_axes_location
.get_population ())) {
382 hb_font_destroy (font
);
386 for (auto _
: plan
->user_axes_location
)
390 var
.value
= _
.second
.middle
;
395 hb_font_set_variations (font
, vars
.arrayZ
, plan
->user_axes_location
.get_population ());
401 _collect_layout_variation_indices (hb_subset_plan_t
* plan
)
403 hb_blob_ptr_t
<OT::GDEF
> gdef
= plan
->source_table
<OT::GDEF
> ();
404 hb_blob_ptr_t
<GPOS
> gpos
= plan
->source_table
<GPOS
> ();
406 if (!gdef
->has_data ())
414 OT::hb_collect_variation_indices_context_t
c (&varidx_set
,
415 &plan
->_glyphset_gsub
,
416 &plan
->gpos_lookups
);
417 gdef
->collect_variation_indices (&c
);
419 if (hb_ot_layout_has_positioning (plan
->source
))
420 gpos
->collect_variation_indices (&c
);
422 gdef
->remap_layout_variation_indices (&varidx_set
,
423 plan
->normalized_coords
,
424 !plan
->pinned_at_default
,
425 plan
->all_axes_pinned
,
426 &plan
->layout_variation_idx_delta_map
);
428 unsigned subtable_count
= gdef
->has_var_store () ? gdef
->get_var_store ().get_sub_table_count () : 0;
429 _generate_varstore_inner_maps (varidx_set
, subtable_count
, plan
->gdef_varstore_inner_maps
);
437 _cmap_closure (hb_face_t
*face
,
438 const hb_set_t
*unicodes
,
441 OT::cmap::accelerator_t
cmap (face
);
442 cmap
.table
->closure_glyphs (unicodes
, glyphset
);
445 static void _colr_closure (hb_face_t
*face
,
446 hb_map_t
*layers_map
,
447 hb_map_t
*palettes_map
,
448 hb_set_t
*glyphs_colred
)
450 OT::COLR::accelerator_t
colr (face
);
451 if (!colr
.is_valid ()) return;
453 hb_set_t palette_indices
, layer_indices
;
454 // Collect all glyphs referenced by COLRv0
455 hb_set_t glyphset_colrv0
;
456 for (hb_codepoint_t gid
: *glyphs_colred
)
457 colr
.closure_glyphs (gid
, &glyphset_colrv0
);
459 glyphs_colred
->union_ (glyphset_colrv0
);
462 colr
.closure_forV1 (glyphs_colred
, &layer_indices
, &palette_indices
);
464 colr
.closure_V0palette_indices (glyphs_colred
, &palette_indices
);
465 _remap_indexes (&layer_indices
, layers_map
);
466 _remap_palette_indexes (&palette_indices
, palettes_map
);
470 _math_closure (hb_subset_plan_t
*plan
,
473 hb_blob_ptr_t
<OT::MATH
> math
= plan
->source_table
<OT::MATH
> ();
474 if (math
->has_data ())
475 math
->closure_glyphs (glyphset
);
480 _remap_used_mark_sets (hb_subset_plan_t
*plan
,
481 hb_map_t
& used_mark_sets_map
)
483 hb_blob_ptr_t
<OT::GDEF
> gdef
= plan
->source_table
<OT::GDEF
> ();
485 if (!gdef
->has_data () || !gdef
->has_mark_glyph_sets ())
491 hb_set_t used_mark_sets
;
492 gdef
->get_mark_glyph_sets ().collect_used_mark_sets (plan
->_glyphset_gsub
, used_mark_sets
);
495 _remap_indexes (&used_mark_sets
, &used_mark_sets_map
);
499 _remove_invalid_gids (hb_set_t
*glyphs
,
500 unsigned int num_glyphs
)
502 glyphs
->del_range (num_glyphs
, HB_SET_VALUE_INVALID
);
506 _populate_unicodes_to_retain (const hb_set_t
*unicodes
,
507 const hb_set_t
*glyphs
,
508 hb_subset_plan_t
*plan
)
510 OT::cmap::accelerator_t
cmap (plan
->source
);
511 unsigned size_threshold
= plan
->source
->get_num_glyphs ();
512 if (glyphs
->is_empty () && unicodes
->get_population () < size_threshold
)
515 const hb_map_t
* unicode_to_gid
= nullptr;
516 if (plan
->accelerator
)
517 unicode_to_gid
= &plan
->accelerator
->unicode_to_gid
;
519 // This is approach to collection is faster, but can only be used if glyphs
520 // are not being explicitly added to the subset and the input unicodes set is
521 // not excessively large (eg. an inverted set).
522 plan
->unicode_to_new_gid_list
.alloc (unicodes
->get_population ());
523 if (!unicode_to_gid
) {
524 for (hb_codepoint_t cp
: *unicodes
)
527 if (!cmap
.get_nominal_glyph (cp
, &gid
))
529 DEBUG_MSG(SUBSET
, nullptr, "Drop U+%04X; no gid", cp
);
533 plan
->codepoint_to_glyph
->set (cp
, gid
);
534 plan
->unicode_to_new_gid_list
.push (hb_pair (cp
, gid
));
537 // Use in memory unicode to gid map it's faster then looking up from
538 // the map. This code is mostly duplicated from above to avoid doing
539 // conditionals on the presence of the unicode_to_gid map each
541 for (hb_codepoint_t cp
: *unicodes
)
543 hb_codepoint_t gid
= unicode_to_gid
->get (cp
);
544 if (gid
== HB_MAP_VALUE_INVALID
)
546 DEBUG_MSG(SUBSET
, nullptr, "Drop U+%04X; no gid", cp
);
550 plan
->codepoint_to_glyph
->set (cp
, gid
);
551 plan
->unicode_to_new_gid_list
.push (hb_pair (cp
, gid
));
557 // This approach is slower, but can handle adding in glyphs to the subset and will match
558 // them with cmap entries.
560 hb_map_t unicode_glyphid_map_storage
;
561 hb_set_t cmap_unicodes_storage
;
562 const hb_map_t
* unicode_glyphid_map
= &unicode_glyphid_map_storage
;
563 const hb_set_t
* cmap_unicodes
= &cmap_unicodes_storage
;
565 if (!plan
->accelerator
) {
566 cmap
.collect_mapping (&cmap_unicodes_storage
, &unicode_glyphid_map_storage
);
567 plan
->unicode_to_new_gid_list
.alloc (hb_min(unicodes
->get_population ()
568 + glyphs
->get_population (),
569 cmap_unicodes
->get_population ()));
571 unicode_glyphid_map
= &plan
->accelerator
->unicode_to_gid
;
572 cmap_unicodes
= &plan
->accelerator
->unicodes
;
575 if (plan
->accelerator
&&
576 unicodes
->get_population () < cmap_unicodes
->get_population () &&
577 glyphs
->get_population () < cmap_unicodes
->get_population ())
579 plan
->codepoint_to_glyph
->alloc (unicodes
->get_population () + glyphs
->get_population ());
581 auto &gid_to_unicodes
= plan
->accelerator
->gid_to_unicodes
;
582 for (hb_codepoint_t gid
: *glyphs
)
584 auto unicodes
= gid_to_unicodes
.get (gid
);
586 for (hb_codepoint_t cp
: unicodes
)
588 plan
->codepoint_to_glyph
->set (cp
, gid
);
589 plan
->unicode_to_new_gid_list
.push (hb_pair (cp
, gid
));
592 for (hb_codepoint_t cp
: *unicodes
)
594 /* Don't double-add entry. */
595 if (plan
->codepoint_to_glyph
->has (cp
))
599 if (!unicode_glyphid_map
->has(cp
, &gid
))
602 plan
->codepoint_to_glyph
->set (cp
, *gid
);
603 plan
->unicode_to_new_gid_list
.push (hb_pair (cp
, *gid
));
605 plan
->unicode_to_new_gid_list
.qsort ();
609 plan
->codepoint_to_glyph
->alloc (cmap_unicodes
->get_population ());
610 hb_codepoint_t first
= HB_SET_VALUE_INVALID
, last
= HB_SET_VALUE_INVALID
;
611 for (; cmap_unicodes
->next_range (&first
, &last
); )
613 for (unsigned cp
= first
; cp
<= last
; cp
++)
615 hb_codepoint_t gid
= (*unicode_glyphid_map
)[cp
];
616 if (!unicodes
->has (cp
) && !glyphs
->has (gid
))
619 plan
->codepoint_to_glyph
->set (cp
, gid
);
620 plan
->unicode_to_new_gid_list
.push (hb_pair (cp
, gid
));
625 /* Add gids which where requested, but not mapped in cmap */
626 unsigned num_glyphs
= plan
->source
->get_num_glyphs ();
627 hb_codepoint_t first
= HB_SET_VALUE_INVALID
, last
= HB_SET_VALUE_INVALID
;
628 for (; glyphs
->next_range (&first
, &last
); )
630 if (first
>= num_glyphs
)
632 if (last
>= num_glyphs
)
633 last
= num_glyphs
- 1;
634 plan
->_glyphset_gsub
.add_range (first
, last
);
638 auto &arr
= plan
->unicode_to_new_gid_list
;
641 plan
->unicodes
.add_sorted_array (&arr
.arrayZ
->first
, arr
.length
, sizeof (*arr
.arrayZ
));
642 plan
->_glyphset_gsub
.add_array (&arr
.arrayZ
->second
, arr
.length
, sizeof (*arr
.arrayZ
));
646 #ifndef HB_COMPOSITE_OPERATIONS_PER_GLYPH
647 #define HB_COMPOSITE_OPERATIONS_PER_GLYPH 64
651 _glyf_add_gid_and_children (const OT::glyf_accelerator_t
&glyf
,
653 hb_set_t
*gids_to_retain
,
657 /* Check if is already visited */
658 if (gids_to_retain
->has (gid
)) return operation_count
;
660 gids_to_retain
->add (gid
);
662 if (unlikely (depth
++ > HB_MAX_NESTING_LEVEL
)) return operation_count
;
663 if (unlikely (--operation_count
< 0)) return operation_count
;
665 auto glyph
= glyf
.glyph_for_gid (gid
);
667 for (auto &item
: glyph
.get_composite_iterator ())
669 _glyf_add_gid_and_children (glyf
,
675 #ifndef HB_NO_VAR_COMPOSITES
676 for (auto &item
: glyph
.get_var_composite_iterator ())
679 _glyf_add_gid_and_children (glyf
,
687 return operation_count
;
691 _nameid_closure (hb_subset_plan_t
* plan
,
692 hb_set_t
* drop_tables
)
695 plan
->source
->table
.STAT
->collect_name_ids (&plan
->user_axes_location
, &plan
->name_ids
);
698 if (!plan
->all_axes_pinned
)
699 plan
->source
->table
.fvar
->collect_name_ids (&plan
->user_axes_location
, &plan
->axes_old_index_tag_map
, &plan
->name_ids
);
702 if (!drop_tables
->has (HB_OT_TAG_CPAL
))
703 plan
->source
->table
.CPAL
->collect_name_ids (&plan
->colr_palettes
, &plan
->name_ids
);
706 #ifndef HB_NO_SUBSET_LAYOUT
707 if (!drop_tables
->has (HB_OT_TAG_GPOS
))
709 hb_blob_ptr_t
<GPOS
> gpos
= plan
->source_table
<GPOS
> ();
710 gpos
->collect_name_ids (&plan
->gpos_features
, &plan
->name_ids
);
713 if (!drop_tables
->has (HB_OT_TAG_GSUB
))
715 hb_blob_ptr_t
<GSUB
> gsub
= plan
->source_table
<GSUB
> ();
716 gsub
->collect_name_ids (&plan
->gsub_features
, &plan
->name_ids
);
723 _populate_gids_to_retain (hb_subset_plan_t
* plan
,
724 hb_set_t
* drop_tables
)
726 OT::glyf_accelerator_t
glyf (plan
->source
);
727 #ifndef HB_NO_SUBSET_CFF
728 // Note: we cannot use inprogress_accelerator here, since it has not been
729 // created yet. So in case of preprocessed-face (and otherwise), we do an
730 // extra sanitize pass here, which is not ideal.
731 OT::cff1::accelerator_subset_t
stack_cff (plan
->accelerator
? nullptr : plan
->source
);
732 const OT::cff1::accelerator_subset_t
*cff (plan
->accelerator
? plan
->accelerator
->cff1_accel
.get () : &stack_cff
);
735 plan
->_glyphset_gsub
.add (0); // Not-def
737 _cmap_closure (plan
->source
, &plan
->unicodes
, &plan
->_glyphset_gsub
);
739 #ifndef HB_NO_SUBSET_LAYOUT
740 if (!drop_tables
->has (HB_OT_TAG_GSUB
))
741 // closure all glyphs/lookups/features needed for GSUB substitutions.
742 _closure_glyphs_lookups_features
<GSUB
> (
744 &plan
->_glyphset_gsub
,
746 &plan
->gsub_features
,
748 &plan
->gsub_feature_record_cond_idx_map
,
749 &plan
->gsub_feature_substitutes_map
,
750 plan
->gsub_old_features
,
751 plan
->gsub_old_feature_idx_tag_map
);
753 if (!drop_tables
->has (HB_OT_TAG_GPOS
))
754 _closure_glyphs_lookups_features
<GPOS
> (
756 &plan
->_glyphset_gsub
,
758 &plan
->gpos_features
,
760 &plan
->gpos_feature_record_cond_idx_map
,
761 &plan
->gpos_feature_substitutes_map
,
762 plan
->gpos_old_features
,
763 plan
->gpos_old_feature_idx_tag_map
);
765 _remove_invalid_gids (&plan
->_glyphset_gsub
, plan
->source
->get_num_glyphs ());
767 plan
->_glyphset_mathed
= plan
->_glyphset_gsub
;
768 if (!drop_tables
->has (HB_OT_TAG_MATH
))
770 _math_closure (plan
, &plan
->_glyphset_mathed
);
771 _remove_invalid_gids (&plan
->_glyphset_mathed
, plan
->source
->get_num_glyphs ());
774 hb_set_t cur_glyphset
= plan
->_glyphset_mathed
;
775 if (!drop_tables
->has (HB_OT_TAG_COLR
))
777 _colr_closure (plan
->source
, &plan
->colrv1_layers
, &plan
->colr_palettes
, &cur_glyphset
);
778 _remove_invalid_gids (&cur_glyphset
, plan
->source
->get_num_glyphs ());
781 plan
->_glyphset_colred
= cur_glyphset
;
783 _nameid_closure (plan
, drop_tables
);
784 /* Populate a full set of glyphs to retain by adding all referenced
785 * composite glyphs. */
786 if (glyf
.has_data ())
787 for (hb_codepoint_t gid
: cur_glyphset
)
788 _glyf_add_gid_and_children (glyf
, gid
, &plan
->_glyphset
,
789 cur_glyphset
.get_population () * HB_COMPOSITE_OPERATIONS_PER_GLYPH
);
791 plan
->_glyphset
.union_ (cur_glyphset
);
792 #ifndef HB_NO_SUBSET_CFF
793 if (!plan
->accelerator
|| plan
->accelerator
->has_seac
)
795 bool has_seac
= false;
796 if (cff
->is_valid ())
797 for (hb_codepoint_t gid
: cur_glyphset
)
798 if (_add_cff_seac_components (*cff
, gid
, &plan
->_glyphset
))
800 plan
->has_seac
= has_seac
;
804 _remove_invalid_gids (&plan
->_glyphset
, plan
->source
->get_num_glyphs ());
807 if (!drop_tables
->has (HB_OT_TAG_GDEF
))
808 _collect_layout_variation_indices (plan
);
813 _create_glyph_map_gsub (const hb_set_t
* glyph_set_gsub
,
814 const hb_map_t
* glyph_map
,
817 out
->alloc (glyph_set_gsub
->get_population ());
818 + hb_iter (glyph_set_gsub
)
819 | hb_map ([&] (hb_codepoint_t gid
) {
820 return hb_codepoint_pair_t (gid
, glyph_map
->get (gid
));
827 _create_old_gid_to_new_gid_map (const hb_face_t
*face
,
829 const hb_set_t
*all_gids_to_retain
,
830 const hb_map_t
*requested_glyph_map
,
831 hb_map_t
*glyph_map
, /* OUT */
832 hb_map_t
*reverse_glyph_map
, /* OUT */
833 hb_sorted_vector_t
<hb_codepoint_pair_t
> *new_to_old_gid_list
/* OUT */,
834 unsigned int *num_glyphs
/* OUT */)
836 unsigned pop
= all_gids_to_retain
->get_population ();
837 reverse_glyph_map
->alloc (pop
);
838 glyph_map
->alloc (pop
);
839 new_to_old_gid_list
->alloc (pop
);
841 if (*requested_glyph_map
)
843 hb_set_t
new_gids(requested_glyph_map
->values());
844 if (new_gids
.get_population() != requested_glyph_map
->get_population())
846 DEBUG_MSG (SUBSET
, nullptr, "The provided custom glyph mapping is not unique.");
852 DEBUG_MSG (SUBSET
, nullptr,
853 "HB_SUBSET_FLAGS_RETAIN_GIDS cannot be set if "
854 "a custom glyph mapping has been provided.");
858 hb_codepoint_t max_glyph
= 0;
860 for (auto old_gid
: all_gids_to_retain
->iter ())
863 new_to_old_gid_list
->push (hb_pair
<hb_codepoint_t
, hb_codepoint_t
> (0u, 0u));
867 hb_codepoint_t
* new_gid
;
868 if (!requested_glyph_map
->has (old_gid
, &new_gid
))
870 remaining
.add(old_gid
);
874 if (*new_gid
> max_glyph
)
875 max_glyph
= *new_gid
;
876 new_to_old_gid_list
->push (hb_pair (*new_gid
, old_gid
));
878 new_to_old_gid_list
->qsort ();
880 // Anything that wasn't mapped by the requested mapping should
881 // be placed after the requested mapping.
882 for (auto old_gid
: remaining
)
883 new_to_old_gid_list
->push (hb_pair (++max_glyph
, old_gid
));
885 *num_glyphs
= max_glyph
+ 1;
887 else if (!retain_gids
)
889 + hb_enumerate (hb_iter (all_gids_to_retain
), (hb_codepoint_t
) 0)
890 | hb_sink (new_to_old_gid_list
)
892 *num_glyphs
= new_to_old_gid_list
->length
;
896 + hb_iter (all_gids_to_retain
)
897 | hb_map ([] (hb_codepoint_t _
) {
898 return hb_codepoint_pair_t (_
, _
);
900 | hb_sink (new_to_old_gid_list
)
903 hb_codepoint_t max_glyph
= HB_SET_VALUE_INVALID
;
904 hb_set_previous (all_gids_to_retain
, &max_glyph
);
906 *num_glyphs
= max_glyph
+ 1;
909 reverse_glyph_map
->alloc (reverse_glyph_map
->get_population () + new_to_old_gid_list
->length
);
910 + hb_iter (new_to_old_gid_list
)
911 | hb_sink (reverse_glyph_map
)
913 glyph_map
->alloc (glyph_map
->get_population () + new_to_old_gid_list
->length
);
914 + hb_iter (new_to_old_gid_list
)
915 | hb_map (&hb_codepoint_pair_t::reverse
)
916 | hb_sink (glyph_map
)
924 _normalize_axes_location (hb_face_t
*face
, hb_subset_plan_t
*plan
)
926 if (plan
->user_axes_location
.is_empty ())
929 hb_array_t
<const OT::AxisRecord
> axes
= face
->table
.fvar
->get_axes ();
930 plan
->normalized_coords
.resize (axes
.length
);
932 bool has_avar
= face
->table
.avar
->has_data ();
933 const OT::SegmentMaps
*seg_maps
= nullptr;
934 unsigned avar_axis_count
= 0;
937 seg_maps
= face
->table
.avar
->get_segment_maps ();
938 avar_axis_count
= face
->table
.avar
->get_axis_count();
941 bool axis_not_pinned
= false;
942 unsigned old_axis_idx
= 0, new_axis_idx
= 0;
943 for (const auto& axis
: axes
)
945 hb_tag_t axis_tag
= axis
.get_axis_tag ();
946 plan
->axes_old_index_tag_map
.set (old_axis_idx
, axis_tag
);
948 if (!plan
->user_axes_location
.has (axis_tag
) ||
949 !plan
->user_axes_location
.get (axis_tag
).is_point ())
951 axis_not_pinned
= true;
952 plan
->axes_index_map
.set (old_axis_idx
, new_axis_idx
);
953 plan
->axis_tags
.push (axis_tag
);
958 if (plan
->user_axes_location
.has (axis_tag
, &axis_range
))
960 plan
->axes_triple_distances
.set (axis_tag
, axis
.get_triple_distances ());
962 int normalized_min
= axis
.normalize_axis_value (axis_range
->minimum
);
963 int normalized_default
= axis
.normalize_axis_value (axis_range
->middle
);
964 int normalized_max
= axis
.normalize_axis_value (axis_range
->maximum
);
966 if (has_avar
&& old_axis_idx
< avar_axis_count
)
968 normalized_min
= seg_maps
->map (normalized_min
);
969 normalized_default
= seg_maps
->map (normalized_default
);
970 normalized_max
= seg_maps
->map (normalized_max
);
972 plan
->axes_location
.set (axis_tag
, Triple (static_cast<float> (normalized_min
/ 16384.f
),
973 static_cast<float> (normalized_default
/ 16384.f
),
974 static_cast<float> (normalized_max
/ 16384.f
)));
976 if (normalized_default
!= 0)
977 plan
->pinned_at_default
= false;
979 plan
->normalized_coords
[old_axis_idx
] = normalized_default
;
984 if (has_avar
&& old_axis_idx
< avar_axis_count
)
985 seg_maps
= &StructAfter
<OT::SegmentMaps
> (*seg_maps
);
987 plan
->all_axes_pinned
= !axis_not_pinned
;
991 _update_instance_metrics_map_from_cff2 (hb_subset_plan_t
*plan
)
993 if (!plan
->normalized_coords
) return;
994 OT::cff2::accelerator_t
cff2 (plan
->source
);
995 if (!cff2
.is_valid ()) return;
997 hb_font_t
*font
= nullptr;
998 if (unlikely (!plan
->check_success (font
= _get_hb_font_with_variations (plan
))))
1000 hb_font_destroy (font
);
1004 hb_glyph_extents_t extents
= {0x7FFF, -0x7FFF};
1005 OT::hmtx_accelerator_t
_hmtx (plan
->source
);
1006 float *hvar_store_cache
= nullptr;
1007 if (_hmtx
.has_data () && _hmtx
.var_table
.get_length ())
1008 hvar_store_cache
= _hmtx
.var_table
->get_var_store ().create_cache ();
1010 OT::vmtx_accelerator_t
_vmtx (plan
->source
);
1011 float *vvar_store_cache
= nullptr;
1012 if (_vmtx
.has_data () && _vmtx
.var_table
.get_length ())
1013 vvar_store_cache
= _vmtx
.var_table
->get_var_store ().create_cache ();
1015 for (auto p
: *plan
->glyph_map
)
1017 hb_codepoint_t old_gid
= p
.first
;
1018 hb_codepoint_t new_gid
= p
.second
;
1019 if (!cff2
.get_extents (font
, old_gid
, &extents
)) continue;
1020 bool has_bounds_info
= true;
1021 if (extents
.x_bearing
== 0 && extents
.width
== 0 &&
1022 extents
.height
== 0 && extents
.y_bearing
== 0)
1023 has_bounds_info
= false;
1025 if (has_bounds_info
)
1027 plan
->head_maxp_info
.xMin
= hb_min (plan
->head_maxp_info
.xMin
, extents
.x_bearing
);
1028 plan
->head_maxp_info
.xMax
= hb_max (plan
->head_maxp_info
.xMax
, extents
.x_bearing
+ extents
.width
);
1029 plan
->head_maxp_info
.yMax
= hb_max (plan
->head_maxp_info
.yMax
, extents
.y_bearing
);
1030 plan
->head_maxp_info
.yMin
= hb_min (plan
->head_maxp_info
.yMin
, extents
.y_bearing
+ extents
.height
);
1033 if (_hmtx
.has_data ())
1035 int hori_aw
= _hmtx
.get_advance_without_var_unscaled (old_gid
);
1036 if (_hmtx
.var_table
.get_length ())
1037 hori_aw
+= (int) roundf (_hmtx
.var_table
->get_advance_delta_unscaled (old_gid
, font
->coords
, font
->num_coords
,
1039 int lsb
= extents
.x_bearing
;
1040 if (!has_bounds_info
)
1042 if (!_hmtx
.get_leading_bearing_without_var_unscaled (old_gid
, &lsb
))
1045 plan
->hmtx_map
.set (new_gid
, hb_pair ((unsigned) hori_aw
, lsb
));
1046 plan
->bounds_width_vec
[new_gid
] = extents
.width
;
1049 if (_vmtx
.has_data ())
1051 int vert_aw
= _vmtx
.get_advance_without_var_unscaled (old_gid
);
1052 if (_vmtx
.var_table
.get_length ())
1053 vert_aw
+= (int) roundf (_vmtx
.var_table
->get_advance_delta_unscaled (old_gid
, font
->coords
, font
->num_coords
,
1056 int tsb
= extents
.y_bearing
;
1057 if (!has_bounds_info
)
1059 if (!_vmtx
.get_leading_bearing_without_var_unscaled (old_gid
, &tsb
))
1062 plan
->vmtx_map
.set (new_gid
, hb_pair ((unsigned) vert_aw
, tsb
));
1063 plan
->bounds_height_vec
[new_gid
] = extents
.height
;
1066 hb_font_destroy (font
);
1067 if (hvar_store_cache
)
1068 _hmtx
.var_table
->get_var_store ().destroy_cache (hvar_store_cache
);
1069 if (vvar_store_cache
)
1070 _vmtx
.var_table
->get_var_store ().destroy_cache (vvar_store_cache
);
1074 _get_instance_glyphs_contour_points (hb_subset_plan_t
*plan
)
1076 /* contour_points vector only needed for updating gvar table (infer delta)
1077 * during partial instancing */
1078 if (plan
->user_axes_location
.is_empty () || plan
->all_axes_pinned
)
1081 OT::glyf_accelerator_t
glyf (plan
->source
);
1083 for (auto &_
: plan
->new_to_old_gid_list
)
1085 hb_codepoint_t new_gid
= _
.first
;
1086 contour_point_vector_t all_points
;
1087 if (new_gid
== 0 && !(plan
->flags
& HB_SUBSET_FLAGS_NOTDEF_OUTLINE
))
1089 if (unlikely (!plan
->new_gid_contour_points_map
.set (new_gid
, all_points
)))
1094 hb_codepoint_t old_gid
= _
.second
;
1095 if (unlikely (!glyf
.glyph_for_gid (old_gid
).get_all_points_without_var (plan
->source
, all_points
)))
1097 if (unlikely (!plan
->new_gid_contour_points_map
.set (new_gid
, all_points
)))
1104 hb_subset_plan_t::hb_subset_plan_t (hb_face_t
*face
,
1105 const hb_subset_input_t
*input
)
1108 flags
= input
->flags
;
1110 unicode_to_new_gid_list
.init ();
1112 name_ids
= *input
->sets
.name_ids
;
1113 name_languages
= *input
->sets
.name_languages
;
1114 layout_features
= *input
->sets
.layout_features
;
1115 layout_scripts
= *input
->sets
.layout_scripts
;
1116 glyphs_requested
= *input
->sets
.glyphs
;
1117 drop_tables
= *input
->sets
.drop_tables
;
1118 no_subset_tables
= *input
->sets
.no_subset_tables
;
1119 source
= hb_face_reference (face
);
1120 dest
= hb_face_builder_create ();
1122 codepoint_to_glyph
= hb_map_create ();
1123 glyph_map
= hb_map_create ();
1124 reverse_glyph_map
= hb_map_create ();
1126 gsub_insert_catch_all_feature_variation_rec
= false;
1127 gpos_insert_catch_all_feature_variation_rec
= false;
1128 gdef_varstore_inner_maps
.init ();
1130 user_axes_location
= input
->axes_location
;
1131 all_axes_pinned
= false;
1132 pinned_at_default
= true;
1133 has_gdef_varstore
= false;
1135 #ifdef HB_EXPERIMENTAL_API
1136 for (auto _
: input
->name_table_overrides
)
1138 hb_bytes_t name_bytes
= _
.second
;
1139 unsigned len
= name_bytes
.length
;
1140 char *name_str
= (char *) hb_malloc (len
);
1141 if (unlikely (!check_success (name_str
)))
1144 hb_memcpy (name_str
, name_bytes
.arrayZ
, len
);
1145 name_table_overrides
.set (_
.first
, hb_bytes_t (name_str
, len
));
1149 void* accel
= hb_face_get_user_data(face
, hb_subset_accelerator_t::user_data_key());
1151 attach_accelerator_data
= input
->attach_accelerator_data
;
1152 force_long_loca
= input
->force_long_loca
;
1153 #ifdef HB_EXPERIMENTAL_API
1154 force_long_loca
= force_long_loca
|| (flags
& HB_SUBSET_FLAGS_IFTB_REQUIREMENTS
);
1158 accelerator
= (hb_subset_accelerator_t
*) accel
;
1160 if (unlikely (in_error ()))
1164 _normalize_axes_location (face
, this);
1167 _populate_unicodes_to_retain (input
->sets
.unicodes
, input
->sets
.glyphs
, this);
1169 _populate_gids_to_retain (this, input
->sets
.drop_tables
);
1170 if (unlikely (in_error ()))
1173 if (!check_success(_create_old_gid_to_new_gid_map(
1175 input
->flags
& HB_SUBSET_FLAGS_RETAIN_GIDS
,
1180 &new_to_old_gid_list
,
1181 &_num_output_glyphs
))) {
1185 _create_glyph_map_gsub (
1190 // Now that we have old to new gid map update the unicode to new gid list.
1191 for (unsigned i
= 0; i
< unicode_to_new_gid_list
.length
; i
++)
1193 // Use raw array access for performance.
1194 unicode_to_new_gid_list
.arrayZ
[i
].second
=
1195 glyph_map
->get(unicode_to_new_gid_list
.arrayZ
[i
].second
);
1198 bounds_width_vec
.resize (_num_output_glyphs
, false);
1199 for (auto &v
: bounds_width_vec
)
1201 bounds_height_vec
.resize (_num_output_glyphs
, false);
1202 for (auto &v
: bounds_height_vec
)
1205 if (!drop_tables
.has (HB_OT_TAG_GDEF
))
1206 _remap_used_mark_sets (this, used_mark_sets_map
);
1208 if (unlikely (in_error ()))
1212 _update_instance_metrics_map_from_cff2 (this);
1213 if (!check_success (_get_instance_glyphs_contour_points (this)))
1217 if (attach_accelerator_data
)
1219 inprogress_accelerator
=
1220 hb_subset_accelerator_t::create (source
,
1221 *codepoint_to_glyph
,
1225 check_success (inprogress_accelerator
);
1228 #define HB_SUBSET_PLAN_MEMBER(Type, Name) check_success (!Name.in_error ());
1229 #include "hb-subset-plan-member-list.hh"
1230 #undef HB_SUBSET_PLAN_MEMBER
1233 hb_subset_plan_t::~hb_subset_plan_t()
1235 hb_face_destroy (dest
);
1237 hb_map_destroy (codepoint_to_glyph
);
1238 hb_map_destroy (glyph_map
);
1239 hb_map_destroy (reverse_glyph_map
);
1240 #ifndef HB_NO_SUBSET_CFF
1244 hb_face_destroy (source
);
1246 #ifdef HB_EXPERIMENTAL_API
1247 for (auto _
: name_table_overrides
.iter_ref ())
1251 if (inprogress_accelerator
)
1252 hb_subset_accelerator_t::destroy ((void*) inprogress_accelerator
);
1257 * hb_subset_plan_create_or_fail:
1258 * @face: font face to create the plan for.
1259 * @input: a #hb_subset_input_t input.
1261 * Computes a plan for subsetting the supplied face according
1262 * to a provided input. The plan describes
1263 * which tables and glyphs should be retained.
1265 * Return value: (transfer full): New subset plan. Destroy with
1266 * hb_subset_plan_destroy(). If there is a failure creating the plan
1267 * nullptr will be returned.
1272 hb_subset_plan_create_or_fail (hb_face_t
*face
,
1273 const hb_subset_input_t
*input
)
1275 hb_subset_plan_t
*plan
;
1276 if (unlikely (!(plan
= hb_object_create
<hb_subset_plan_t
> (face
, input
))))
1279 if (unlikely (plan
->in_error ()))
1281 hb_subset_plan_destroy (plan
);
1289 * hb_subset_plan_destroy:
1290 * @plan: a #hb_subset_plan_t
1292 * Decreases the reference count on @plan, and if it reaches zero, destroys
1293 * @plan, freeing all memory.
1298 hb_subset_plan_destroy (hb_subset_plan_t
*plan
)
1300 if (!hb_object_destroy (plan
)) return;
1306 * hb_subset_plan_old_to_new_glyph_mapping:
1307 * @plan: a subsetting plan.
1309 * Returns the mapping between glyphs in the original font to glyphs in the
1310 * subset that will be produced by @plan
1312 * Return value: (transfer none):
1313 * A pointer to the #hb_map_t of the mapping.
1318 hb_subset_plan_old_to_new_glyph_mapping (const hb_subset_plan_t
*plan
)
1320 return plan
->glyph_map
;
1324 * hb_subset_plan_new_to_old_glyph_mapping:
1325 * @plan: a subsetting plan.
1327 * Returns the mapping between glyphs in the subset that will be produced by
1328 * @plan and the glyph in the original font.
1330 * Return value: (transfer none):
1331 * A pointer to the #hb_map_t of the mapping.
1336 hb_subset_plan_new_to_old_glyph_mapping (const hb_subset_plan_t
*plan
)
1338 return plan
->reverse_glyph_map
;
1342 * hb_subset_plan_unicode_to_old_glyph_mapping:
1343 * @plan: a subsetting plan.
1345 * Returns the mapping between codepoints in the original font and the
1346 * associated glyph id in the original font.
1348 * Return value: (transfer none):
1349 * A pointer to the #hb_map_t of the mapping.
1354 hb_subset_plan_unicode_to_old_glyph_mapping (const hb_subset_plan_t
*plan
)
1356 return plan
->codepoint_to_glyph
;
1360 * hb_subset_plan_reference: (skip)
1361 * @plan: a #hb_subset_plan_t object.
1363 * Increases the reference count on @plan.
1365 * Return value: @plan.
1370 hb_subset_plan_reference (hb_subset_plan_t
*plan
)
1372 return hb_object_reference (plan
);
1376 * hb_subset_plan_set_user_data: (skip)
1377 * @plan: a #hb_subset_plan_t object.
1378 * @key: The user-data key to set
1379 * @data: A pointer to the user data
1380 * @destroy: (nullable): A callback to call when @data is not needed anymore
1381 * @replace: Whether to replace an existing data with the same key
1383 * Attaches a user-data key/data pair to the given subset plan object.
1385 * Return value: `true` if success, `false` otherwise
1390 hb_subset_plan_set_user_data (hb_subset_plan_t
*plan
,
1391 hb_user_data_key_t
*key
,
1393 hb_destroy_func_t destroy
,
1396 return hb_object_set_user_data (plan
, key
, data
, destroy
, replace
);
1400 * hb_subset_plan_get_user_data: (skip)
1401 * @plan: a #hb_subset_plan_t object.
1402 * @key: The user-data key to query
1404 * Fetches the user data associated with the specified key,
1405 * attached to the specified subset plan object.
1407 * Return value: (transfer none): A pointer to the user data
1412 hb_subset_plan_get_user_data (const hb_subset_plan_t
*plan
,
1413 hb_user_data_key_t
*key
)
1415 return hb_object_get_user_data (plan
, key
);