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