• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright © 2018  Google, Inc.
3  *
4  *  This is part of HarfBuzz, a text shaping library.
5  *
6  * Permission is hereby granted, without written agreement and without
7  * license or royalty fees, to use, copy, modify, and distribute this
8  * software and its documentation for any purpose, provided that the
9  * above copyright notice and the following two paragraphs appear in
10  * all copies of this software.
11  *
12  * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
13  * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
14  * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
15  * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
16  * DAMAGE.
17  *
18  * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
19  * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
20  * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
21  * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
22  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
23  *
24  * Google Author(s): Garret Rieger, Roderick Sheeter
25  */
26 
27 #include "hb-subset-plan.hh"
28 #include "hb-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