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-map.hh"
29 #include "hb-set.hh"
30
31 #include "hb-ot-cmap-table.hh"
32 #include "hb-ot-glyf-table.hh"
33 #include "hb-ot-layout-gdef-table.hh"
34 #include "hb-ot-layout-gpos-table.hh"
35 #include "hb-ot-layout-gsub-table.hh"
36 #include "hb-ot-cff1-table.hh"
37 #include "hb-ot-color-colr-table.hh"
38 #include "hb-ot-var-fvar-table.hh"
39 #include "hb-ot-stat-table.hh"
40
41
42 typedef hb_hashmap_t<unsigned, hb_set_t *, (unsigned)-1, nullptr> script_langsys_map;
43 #ifndef HB_NO_SUBSET_CFF
44 static inline void
_add_cff_seac_components(const OT::cff1::accelerator_t & cff,hb_codepoint_t gid,hb_set_t * gids_to_retain)45 _add_cff_seac_components (const OT::cff1::accelerator_t &cff,
46 hb_codepoint_t gid,
47 hb_set_t *gids_to_retain)
48 {
49 hb_codepoint_t base_gid, accent_gid;
50 if (cff.get_seac_components (gid, &base_gid, &accent_gid))
51 {
52 gids_to_retain->add (base_gid);
53 gids_to_retain->add (accent_gid);
54 }
55 }
56 #endif
57
58 #ifndef HB_NO_SUBSET_LAYOUT
59 static void
_remap_indexes(const hb_set_t * indexes,hb_map_t * mapping)60 _remap_indexes (const hb_set_t *indexes,
61 hb_map_t *mapping /* OUT */)
62 {
63 unsigned count = indexes->get_population ();
64
65 for (auto _ : + hb_zip (indexes->iter (), hb_range (count)))
66 mapping->set (_.first, _.second);
67
68 }
69
70 static inline void
_gsub_closure_glyphs_lookups_features(hb_face_t * face,hb_set_t * gids_to_retain,hb_map_t * gsub_lookups,hb_map_t * gsub_features,script_langsys_map * gsub_langsys)71 _gsub_closure_glyphs_lookups_features (hb_face_t *face,
72 hb_set_t *gids_to_retain,
73 hb_map_t *gsub_lookups,
74 hb_map_t *gsub_features,
75 script_langsys_map *gsub_langsys)
76 {
77 hb_set_t lookup_indices;
78 hb_ot_layout_collect_lookups (face,
79 HB_OT_TAG_GSUB,
80 nullptr,
81 nullptr,
82 nullptr,
83 &lookup_indices);
84 hb_ot_layout_lookups_substitute_closure (face,
85 &lookup_indices,
86 gids_to_retain);
87 hb_blob_ptr_t<OT::GSUB> gsub = hb_sanitize_context_t ().reference_table<OT::GSUB> (face);
88 gsub->closure_lookups (face,
89 gids_to_retain,
90 &lookup_indices);
91 _remap_indexes (&lookup_indices, gsub_lookups);
92
93 // Collect and prune features
94 hb_set_t feature_indices;
95 hb_ot_layout_collect_features (face,
96 HB_OT_TAG_GSUB,
97 nullptr,
98 nullptr,
99 nullptr,
100 &feature_indices);
101
102 gsub->prune_features (gsub_lookups, &feature_indices);
103 hb_map_t duplicate_feature_map;
104 gsub->find_duplicate_features (gsub_lookups, &feature_indices, &duplicate_feature_map);
105
106 feature_indices.clear ();
107 gsub->prune_langsys (&duplicate_feature_map, gsub_langsys, &feature_indices);
108 _remap_indexes (&feature_indices, gsub_features);
109
110 gsub.destroy ();
111 }
112
113 static inline void
_gpos_closure_lookups_features(hb_face_t * face,const hb_set_t * gids_to_retain,hb_map_t * gpos_lookups,hb_map_t * gpos_features,script_langsys_map * gpos_langsys)114 _gpos_closure_lookups_features (hb_face_t *face,
115 const hb_set_t *gids_to_retain,
116 hb_map_t *gpos_lookups,
117 hb_map_t *gpos_features,
118 script_langsys_map *gpos_langsys)
119 {
120 hb_set_t lookup_indices;
121 hb_ot_layout_collect_lookups (face,
122 HB_OT_TAG_GPOS,
123 nullptr,
124 nullptr,
125 nullptr,
126 &lookup_indices);
127 hb_blob_ptr_t<OT::GPOS> gpos = hb_sanitize_context_t ().reference_table<OT::GPOS> (face);
128 gpos->closure_lookups (face,
129 gids_to_retain,
130 &lookup_indices);
131 _remap_indexes (&lookup_indices, gpos_lookups);
132
133 // Collect and prune features
134 hb_set_t feature_indices;
135 hb_ot_layout_collect_features (face,
136 HB_OT_TAG_GPOS,
137 nullptr,
138 nullptr,
139 nullptr,
140 &feature_indices);
141
142 gpos->prune_features (gpos_lookups, &feature_indices);
143 hb_map_t duplicate_feature_map;
144 gpos->find_duplicate_features (gpos_lookups, &feature_indices, &duplicate_feature_map);
145
146 feature_indices.clear ();
147 gpos->prune_langsys (&duplicate_feature_map, gpos_langsys, &feature_indices);
148 _remap_indexes (&feature_indices, gpos_features);
149
150 gpos.destroy ();
151 }
152 #endif
153
154 #ifndef HB_NO_VAR
155 static inline void
_collect_layout_variation_indices(hb_face_t * face,const hb_set_t * glyphset,const hb_map_t * gpos_lookups,hb_set_t * layout_variation_indices,hb_map_t * layout_variation_idx_map)156 _collect_layout_variation_indices (hb_face_t *face,
157 const hb_set_t *glyphset,
158 const hb_map_t *gpos_lookups,
159 hb_set_t *layout_variation_indices,
160 hb_map_t *layout_variation_idx_map)
161 {
162 hb_blob_ptr_t<OT::GDEF> gdef = hb_sanitize_context_t ().reference_table<OT::GDEF> (face);
163 hb_blob_ptr_t<OT::GPOS> gpos = hb_sanitize_context_t ().reference_table<OT::GPOS> (face);
164
165 if (!gdef->has_data ())
166 {
167 gdef.destroy ();
168 gpos.destroy ();
169 return;
170 }
171 OT::hb_collect_variation_indices_context_t c (layout_variation_indices, glyphset, gpos_lookups);
172 gdef->collect_variation_indices (&c);
173
174 if (hb_ot_layout_has_positioning (face))
175 gpos->collect_variation_indices (&c);
176
177 gdef->remap_layout_variation_indices (layout_variation_indices, layout_variation_idx_map);
178
179 gdef.destroy ();
180 gpos.destroy ();
181 }
182 #endif
183
184 static inline void
_cmap_closure(hb_face_t * face,const hb_set_t * unicodes,hb_set_t * glyphset)185 _cmap_closure (hb_face_t *face,
186 const hb_set_t *unicodes,
187 hb_set_t *glyphset)
188 {
189 OT::cmap::accelerator_t cmap;
190 cmap.init (face);
191 cmap.table->closure_glyphs (unicodes, glyphset);
192 cmap.fini ();
193 }
194
195 static inline void
_remove_invalid_gids(hb_set_t * glyphs,unsigned int num_glyphs)196 _remove_invalid_gids (hb_set_t *glyphs,
197 unsigned int num_glyphs)
198 {
199 hb_codepoint_t gid = HB_SET_VALUE_INVALID;
200 while (glyphs->next (&gid))
201 {
202 if (gid >= num_glyphs)
203 glyphs->del (gid);
204 }
205 }
206
207 static void
_populate_gids_to_retain(hb_subset_plan_t * plan,const hb_set_t * unicodes,const hb_set_t * input_glyphs_to_retain,bool close_over_gsub,bool close_over_gpos,bool close_over_gdef)208 _populate_gids_to_retain (hb_subset_plan_t* plan,
209 const hb_set_t *unicodes,
210 const hb_set_t *input_glyphs_to_retain,
211 bool close_over_gsub,
212 bool close_over_gpos,
213 bool close_over_gdef)
214 {
215 OT::cmap::accelerator_t cmap;
216 OT::glyf::accelerator_t glyf;
217 #ifndef HB_NO_SUBSET_CFF
218 OT::cff1::accelerator_t cff;
219 #endif
220 OT::COLR::accelerator_t colr;
221 cmap.init (plan->source);
222 glyf.init (plan->source);
223 #ifndef HB_NO_SUBSET_CFF
224 cff.init (plan->source);
225 #endif
226 colr.init (plan->source);
227
228 plan->_glyphset_gsub->add (0); // Not-def
229 hb_set_union (plan->_glyphset_gsub, input_glyphs_to_retain);
230
231 hb_codepoint_t cp = HB_SET_VALUE_INVALID;
232 while (unicodes->next (&cp))
233 {
234 hb_codepoint_t gid;
235 if (!cmap.get_nominal_glyph (cp, &gid))
236 {
237 DEBUG_MSG(SUBSET, nullptr, "Drop U+%04X; no gid", cp);
238 continue;
239 }
240 plan->unicodes->add (cp);
241 plan->codepoint_to_glyph->set (cp, gid);
242 plan->_glyphset_gsub->add (gid);
243 }
244
245 _cmap_closure (plan->source, plan->unicodes, plan->_glyphset_gsub);
246
247 #ifndef HB_NO_SUBSET_LAYOUT
248 if (close_over_gsub)
249 // closure all glyphs/lookups/features needed for GSUB substitutions.
250 _gsub_closure_glyphs_lookups_features (plan->source, plan->_glyphset_gsub, plan->gsub_lookups, plan->gsub_features, plan->gsub_langsys);
251
252 if (close_over_gpos)
253 _gpos_closure_lookups_features (plan->source, plan->_glyphset_gsub, plan->gpos_lookups, plan->gpos_features, plan->gpos_langsys);
254 #endif
255 _remove_invalid_gids (plan->_glyphset_gsub, plan->source->get_num_glyphs ());
256
257 // Populate a full set of glyphs to retain by adding all referenced
258 // composite glyphs.
259 hb_codepoint_t gid = HB_SET_VALUE_INVALID;
260 while (plan->_glyphset_gsub->next (&gid))
261 {
262 glyf.add_gid_and_children (gid, plan->_glyphset);
263 #ifndef HB_NO_SUBSET_CFF
264 if (cff.is_valid ())
265 _add_cff_seac_components (cff, gid, plan->_glyphset);
266 #endif
267 if (colr.is_valid ())
268 colr.closure_glyphs (gid, plan->_glyphset);
269 }
270
271 _remove_invalid_gids (plan->_glyphset, plan->source->get_num_glyphs ());
272
273 #ifndef HB_NO_VAR
274 if (close_over_gdef)
275 _collect_layout_variation_indices (plan->source,
276 plan->_glyphset_gsub,
277 plan->gpos_lookups,
278 plan->layout_variation_indices,
279 plan->layout_variation_idx_map);
280 #endif
281
282 #ifndef HB_NO_SUBSET_CFF
283 cff.fini ();
284 #endif
285 glyf.fini ();
286 cmap.fini ();
287 }
288
289 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)290 _create_old_gid_to_new_gid_map (const hb_face_t *face,
291 bool retain_gids,
292 const hb_set_t *all_gids_to_retain,
293 hb_map_t *glyph_map, /* OUT */
294 hb_map_t *reverse_glyph_map, /* OUT */
295 unsigned int *num_glyphs /* OUT */)
296 {
297 if (!retain_gids)
298 {
299 + hb_enumerate (hb_iter (all_gids_to_retain), (hb_codepoint_t) 0)
300 | hb_sink (reverse_glyph_map)
301 ;
302 *num_glyphs = reverse_glyph_map->get_population ();
303 } else {
304 + hb_iter (all_gids_to_retain)
305 | hb_map ([] (hb_codepoint_t _) {
306 return hb_pair_t<hb_codepoint_t, hb_codepoint_t> (_, _);
307 })
308 | hb_sink (reverse_glyph_map)
309 ;
310
311 unsigned max_glyph =
312 + hb_iter (all_gids_to_retain)
313 | hb_reduce (hb_max, 0u)
314 ;
315 *num_glyphs = max_glyph + 1;
316 }
317
318 + reverse_glyph_map->iter ()
319 | hb_map (&hb_pair_t<hb_codepoint_t, hb_codepoint_t>::reverse)
320 | hb_sink (glyph_map)
321 ;
322 }
323
324 static void
_nameid_closure(hb_face_t * face,hb_set_t * nameids)325 _nameid_closure (hb_face_t *face,
326 hb_set_t *nameids)
327 {
328 #ifndef HB_NO_STYLE
329 face->table.STAT->collect_name_ids (nameids);
330 #endif
331 #ifndef HB_NO_VAR
332 face->table.fvar->collect_name_ids (nameids);
333 #endif
334 }
335
336 /**
337 * hb_subset_plan_create:
338 * Computes a plan for subsetting the supplied face according
339 * to a provided input. The plan describes
340 * which tables and glyphs should be retained.
341 *
342 * Return value: New subset plan.
343 *
344 * Since: 1.7.5
345 **/
346 hb_subset_plan_t *
hb_subset_plan_create(hb_face_t * face,hb_subset_input_t * input)347 hb_subset_plan_create (hb_face_t *face,
348 hb_subset_input_t *input)
349 {
350 hb_subset_plan_t *plan;
351 if (unlikely (!(plan = hb_object_create<hb_subset_plan_t> ())))
352 return const_cast<hb_subset_plan_t *> (&Null (hb_subset_plan_t));
353
354 plan->successful = true;
355 plan->drop_hints = input->drop_hints;
356 plan->desubroutinize = input->desubroutinize;
357 plan->retain_gids = input->retain_gids;
358 plan->name_legacy = input->name_legacy;
359 plan->unicodes = hb_set_create ();
360 plan->name_ids = hb_set_reference (input->name_ids);
361 _nameid_closure (face, plan->name_ids);
362 plan->name_languages = hb_set_reference (input->name_languages);
363 plan->glyphs_requested = hb_set_reference (input->glyphs);
364 plan->drop_tables = hb_set_reference (input->drop_tables);
365 plan->source = hb_face_reference (face);
366 plan->dest = hb_face_builder_create ();
367
368 plan->_glyphset = hb_set_create ();
369 plan->_glyphset_gsub = hb_set_create ();
370 plan->codepoint_to_glyph = hb_map_create ();
371 plan->glyph_map = hb_map_create ();
372 plan->reverse_glyph_map = hb_map_create ();
373 plan->gsub_lookups = hb_map_create ();
374 plan->gpos_lookups = hb_map_create ();
375
376 if (plan->check_success (plan->gsub_langsys = hb_object_create<script_langsys_map> ()))
377 plan->gsub_langsys->init_shallow ();
378 if (plan->check_success (plan->gpos_langsys = hb_object_create<script_langsys_map> ()))
379 plan->gpos_langsys->init_shallow ();
380
381 plan->gsub_features = hb_map_create ();
382 plan->gpos_features = hb_map_create ();
383 plan->layout_variation_indices = hb_set_create ();
384 plan->layout_variation_idx_map = hb_map_create ();
385
386 if (plan->in_error ()) {
387 return plan;
388 }
389
390 _populate_gids_to_retain (plan,
391 input->unicodes,
392 input->glyphs,
393 !input->drop_tables->has (HB_OT_TAG_GSUB),
394 !input->drop_tables->has (HB_OT_TAG_GPOS),
395 !input->drop_tables->has (HB_OT_TAG_GDEF));
396
397 _create_old_gid_to_new_gid_map (face,
398 input->retain_gids,
399 plan->_glyphset,
400 plan->glyph_map,
401 plan->reverse_glyph_map,
402 &plan->_num_output_glyphs);
403
404 return plan;
405 }
406
407 /**
408 * hb_subset_plan_destroy:
409 *
410 * Since: 1.7.5
411 **/
412 void
hb_subset_plan_destroy(hb_subset_plan_t * plan)413 hb_subset_plan_destroy (hb_subset_plan_t *plan)
414 {
415 if (!hb_object_destroy (plan)) return;
416
417 hb_set_destroy (plan->unicodes);
418 hb_set_destroy (plan->name_ids);
419 hb_set_destroy (plan->name_languages);
420 hb_set_destroy (plan->glyphs_requested);
421 hb_set_destroy (plan->drop_tables);
422 hb_face_destroy (plan->source);
423 hb_face_destroy (plan->dest);
424 hb_map_destroy (plan->codepoint_to_glyph);
425 hb_map_destroy (plan->glyph_map);
426 hb_map_destroy (plan->reverse_glyph_map);
427 hb_set_destroy (plan->_glyphset);
428 hb_set_destroy (plan->_glyphset_gsub);
429 hb_map_destroy (plan->gsub_lookups);
430 hb_map_destroy (plan->gpos_lookups);
431 hb_map_destroy (plan->gsub_features);
432 hb_map_destroy (plan->gpos_features);
433 hb_set_destroy (plan->layout_variation_indices);
434 hb_map_destroy (plan->layout_variation_idx_map);
435
436 if (plan->gsub_langsys)
437 {
438 for (auto _ : plan->gsub_langsys->iter ())
439 hb_set_destroy (_.second);
440
441 hb_object_destroy (plan->gsub_langsys);
442 plan->gsub_langsys->fini_shallow ();
443 free (plan->gsub_langsys);
444 }
445
446 if (plan->gpos_langsys)
447 {
448 for (auto _ : plan->gpos_langsys->iter ())
449 hb_set_destroy (_.second);
450
451 hb_object_destroy (plan->gpos_langsys);
452 plan->gpos_langsys->fini_shallow ();
453 free (plan->gpos_langsys);
454 }
455
456 free (plan);
457 }
458