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