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-cff1-table.hh"
34#include "hb-ot-var-fvar-table.hh"
35#include "hb-ot-stat-table.hh"
36
37#ifndef HB_NO_SUBSET_CFF
38static inline void
39_add_cff_seac_components (const OT::cff1::accelerator_t &cff,
40 hb_codepoint_t gid,
41 hb_set_t *gids_to_retain)
42{
43 hb_codepoint_t base_gid, accent_gid;
44 if (cff.get_seac_components (gid, &base_gid, &accent_gid))
45 {
46 gids_to_retain->add (base_gid);
47 gids_to_retain->add (accent_gid);
48 }
49}
50#endif
51
52#ifndef HB_NO_SUBSET_LAYOUT
53static inline void
54_gsub_closure (hb_face_t *face, hb_set_t *gids_to_retain)
55{
56 hb_set_t lookup_indices;
57 hb_ot_layout_collect_lookups (face,
58 HB_OT_TAG_GSUB,
59 nullptr,
60 nullptr,
61 nullptr,
62 &lookup_indices);
63 hb_ot_layout_lookups_substitute_closure (face,
64 &lookup_indices,
65 gids_to_retain);
66}
67#endif
68
69static inline void
70_cmap_closure (hb_face_t *face,
71 const hb_set_t *unicodes,
72 hb_set_t *glyphset)
73{
74 OT::cmap::accelerator_t cmap;
75 cmap.init (face);
76 cmap.table->closure_glyphs (unicodes, glyphset);
77 cmap.fini ();
78}
79
80static inline void
81_remove_invalid_gids (hb_set_t *glyphs,
82 unsigned int num_glyphs)
83{
84 hb_codepoint_t gid = HB_SET_VALUE_INVALID;
85 while (glyphs->next (&gid))
86 {
87 if (gid >= num_glyphs)
88 glyphs->del (gid);
89 }
90}
91
92static void
93_populate_gids_to_retain (hb_subset_plan_t* plan,
94 const hb_set_t *unicodes,
95 const hb_set_t *input_glyphs_to_retain,
96 bool close_over_gsub)
97{
98 OT::cmap::accelerator_t cmap;
99 OT::glyf::accelerator_t glyf;
100 OT::cff1::accelerator_t cff;
101 cmap.init (plan->source);
102 glyf.init (plan->source);
103 cff.init (plan->source);
104
105 plan->_glyphset_gsub->add (0); // Not-def
106 hb_set_union (plan->_glyphset_gsub, input_glyphs_to_retain);
107
108 hb_codepoint_t cp = HB_SET_VALUE_INVALID;
109 while (unicodes->next (&cp))
110 {
111 hb_codepoint_t gid;
112 if (!cmap.get_nominal_glyph (cp, &gid))
113 {
114 DEBUG_MSG(SUBSET, nullptr, "Drop U+%04X; no gid", cp);
115 continue;
116 }
117 plan->unicodes->add (cp);
118 plan->codepoint_to_glyph->set (cp, gid);
119 plan->_glyphset_gsub->add (gid);
120 }
121
122 _cmap_closure (plan->source, plan->unicodes, plan->_glyphset_gsub);
123
124#ifndef HB_NO_SUBSET_LAYOUT
125 if (close_over_gsub)
126 // Add all glyphs needed for GSUB substitutions.
127 _gsub_closure (plan->source, plan->_glyphset_gsub);
128#endif
129 _remove_invalid_gids (plan->_glyphset_gsub, plan->source->get_num_glyphs ());
130
131 // Populate a full set of glyphs to retain by adding all referenced
132 // composite glyphs.
133 hb_codepoint_t gid = HB_SET_VALUE_INVALID;
134 while (plan->_glyphset_gsub->next (&gid))
135 {
136 glyf.add_gid_and_children (gid, plan->_glyphset);
137#ifndef HB_NO_SUBSET_CFF
138 if (cff.is_valid ())
139 _add_cff_seac_components (cff, gid, plan->_glyphset);
140#endif
141 }
142
143 _remove_invalid_gids (plan->_glyphset, plan->source->get_num_glyphs ());
144
145 cff.fini ();
146 glyf.fini ();
147 cmap.fini ();
148}
149
150static void
151_create_old_gid_to_new_gid_map (const hb_face_t *face,
152 bool retain_gids,
153 const hb_set_t *all_gids_to_retain,
154 hb_map_t *glyph_map, /* OUT */
155 hb_map_t *reverse_glyph_map, /* OUT */
156 unsigned int *num_glyphs /* OUT */)
157{
158 if (!retain_gids)
159 {
160 + hb_enumerate (hb_iter (all_gids_to_retain), (hb_codepoint_t) 0)
161 | hb_sink (reverse_glyph_map)
162 ;
163 *num_glyphs = reverse_glyph_map->get_population ();
164 } else {
165 + hb_iter (all_gids_to_retain)
166 | hb_map ([] (hb_codepoint_t _) {
167 return hb_pair_t<hb_codepoint_t, hb_codepoint_t> (_, _);
168 })
169 | hb_sink (reverse_glyph_map)
170 ;
171
172 unsigned max_glyph =
173 + hb_iter (all_gids_to_retain)
174 | hb_reduce (hb_max, 0u)
175 ;
176 *num_glyphs = max_glyph + 1;
177 }
178
179 + reverse_glyph_map->iter ()
180 | hb_map (&hb_pair_t<hb_codepoint_t, hb_codepoint_t>::reverse)
181 | hb_sink (glyph_map)
182 ;
183}
184
185static void
186_nameid_closure (hb_face_t *face,
187 hb_set_t *nameids)
188{
189#ifndef HB_NO_STAT
190 face->table.STAT->collect_name_ids (nameids);
191#endif
192#ifndef HB_NO_VAR
193 face->table.fvar->collect_name_ids (nameids);
194#endif
195}
196
197/**
198 * hb_subset_plan_create:
199 * Computes a plan for subsetting the supplied face according
200 * to a provided input. The plan describes
201 * which tables and glyphs should be retained.
202 *
203 * Return value: New subset plan.
204 *
205 * Since: 1.7.5
206 **/
207hb_subset_plan_t *
208hb_subset_plan_create (hb_face_t *face,
209 hb_subset_input_t *input)
210{
211 hb_subset_plan_t *plan = hb_object_create<hb_subset_plan_t> ();
212
213 plan->drop_hints = input->drop_hints;
214 plan->desubroutinize = input->desubroutinize;
215 plan->retain_gids = input->retain_gids;
216 plan->unicodes = hb_set_create ();
217 plan->name_ids = hb_set_reference (input->name_ids);
218 _nameid_closure (face, plan->name_ids);
219 plan->drop_tables = hb_set_reference (input->drop_tables);
220 plan->source = hb_face_reference (face);
221 plan->dest = hb_face_builder_create ();
222
223 plan->_glyphset = hb_set_create ();
224 plan->_glyphset_gsub = hb_set_create ();
225 plan->codepoint_to_glyph = hb_map_create ();
226 plan->glyph_map = hb_map_create ();
227 plan->reverse_glyph_map = hb_map_create ();
228
229 _populate_gids_to_retain (plan,
230 input->unicodes,
231 input->glyphs,
232 !input->drop_tables->has (HB_OT_TAG_GSUB));
233
234 _create_old_gid_to_new_gid_map (face,
235 input->retain_gids,
236 plan->_glyphset,
237 plan->glyph_map,
238 plan->reverse_glyph_map,
239 &plan->_num_output_glyphs);
240
241 return plan;
242}
243
244/**
245 * hb_subset_plan_destroy:
246 *
247 * Since: 1.7.5
248 **/
249void
250hb_subset_plan_destroy (hb_subset_plan_t *plan)
251{
252 if (!hb_object_destroy (plan)) return;
253
254 hb_set_destroy (plan->unicodes);
255 hb_set_destroy (plan->name_ids);
256 hb_set_destroy (plan->drop_tables);
257 hb_face_destroy (plan->source);
258 hb_face_destroy (plan->dest);
259 hb_map_destroy (plan->codepoint_to_glyph);
260 hb_map_destroy (plan->glyph_map);
261 hb_map_destroy (plan->reverse_glyph_map);
262 hb_set_destroy (plan->_glyphset);
263 hb_set_destroy (plan->_glyphset_gsub);
264
265 free (plan);
266}
267