Coverage Report

Created: 2026-02-14 06:46

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/harfbuzz/src/hb-subset-plan.cc
Line
Count
Source
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
#include "hb-subset.h"
33
#include "hb-unicode.h"
34
35
#include "hb-ot-cmap-table.hh"
36
#include "hb-ot-glyf-table.hh"
37
#include "hb-ot-layout-base-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-stat-table.hh"
45
#include "hb-ot-math-table.hh"
46
47
hb_subset_accelerator_t::~hb_subset_accelerator_t ()
48
0
{
49
0
  if (cmap_cache && destroy_cmap_cache)
50
0
    destroy_cmap_cache ((void*) cmap_cache);
51
52
0
#ifndef HB_NO_SUBSET_CFF
53
0
  cff1_accel.fini ();
54
0
  cff2_accel.fini ();
55
0
#endif
56
0
  hb_face_destroy (source);
57
0
}
58
59
60
#ifndef HB_NO_SUBSET_CFF
61
static inline bool
62
_add_cff_seac_components (const OT::cff1::accelerator_subset_t &cff,
63
        hb_codepoint_t gid,
64
        hb_set_t *gids_to_retain)
65
7.83k
{
66
7.83k
  hb_codepoint_t base_gid, accent_gid;
67
7.83k
  if (cff.get_seac_components (gid, &base_gid, &accent_gid))
68
58
  {
69
58
    gids_to_retain->add (base_gid);
70
58
    gids_to_retain->add (accent_gid);
71
58
    return true;
72
58
  }
73
7.77k
  return false;
74
7.83k
}
75
#endif
76
77
static void
78
_remap_palette_indexes (const hb_set_t *palette_indexes,
79
      hb_map_t       *mapping /* OUT */)
80
4.98k
{
81
4.98k
  unsigned new_idx = 0;
82
4.98k
  for (unsigned palette_index : palette_indexes->iter ())
83
426k
  {
84
426k
    if (palette_index == 0xFFFF)
85
2.72k
    {
86
2.72k
      mapping->set (palette_index, palette_index);
87
2.72k
      continue;
88
2.72k
    }
89
423k
    mapping->set (palette_index, new_idx);
90
423k
    new_idx++;
91
423k
  }
92
4.98k
}
93
94
void
95
remap_indexes (const hb_set_t *indexes,
96
               hb_map_t       *mapping /* OUT */)
97
78.6k
{
98
78.6k
  for (auto _ : + hb_enumerate (indexes->iter ()))
99
97.3k
    mapping->set (_.second, _.first);
100
78.6k
}
101
102
static inline void
103
_cmap_closure (hb_face_t     *face,
104
         const hb_set_t    *unicodes,
105
         hb_set_t      *glyphset)
106
35.6k
{
107
35.6k
  OT::cmap::accelerator_t cmap (face);
108
35.6k
  cmap.table->closure_glyphs (unicodes, glyphset);
109
35.6k
}
110
111
static void _colr_closure (hb_subset_plan_t* plan,
112
                           hb_set_t *glyphs_colred)
113
35.6k
{
114
35.6k
  OT::COLR::accelerator_t colr (plan->source);
115
35.6k
  if (!colr.is_valid ()) return;
116
117
4.98k
  hb_set_t palette_indices, layer_indices;
118
  // Collect all glyphs referenced by COLRv0
119
4.98k
  hb_set_t glyphset_colrv0;
120
4.98k
  for (hb_codepoint_t gid : *glyphs_colred)
121
11.3k
    colr.closure_glyphs (gid, &glyphset_colrv0);
122
123
4.98k
  glyphs_colred->union_ (glyphset_colrv0);
124
125
  //closure for COLRv1
126
4.98k
  hb_set_t variation_indices, delta_set_indices;
127
4.98k
  colr.closure_forV1 (glyphs_colred, &layer_indices, &palette_indices, &variation_indices, &delta_set_indices);
128
129
4.98k
  colr.closure_V0palette_indices (glyphs_colred, &palette_indices);
130
4.98k
  remap_indexes (&layer_indices, &plan->colrv1_layers);
131
4.98k
  _remap_palette_indexes (&palette_indices, &plan->colr_palettes);
132
133
4.98k
#ifndef HB_NO_VAR
134
4.98k
  if (!colr.has_var_store () || !variation_indices) return;
135
136
36
  const OT::ItemVariationStore &var_store = colr.get_var_store ();
137
  // generated inner_maps is used by ItemVariationStore serialize(), which is subset only
138
36
  unsigned subtable_count = var_store.get_sub_table_count ();
139
36
  generate_varstore_inner_maps (variation_indices, subtable_count, plan->colrv1_varstore_inner_maps);
140
141
  /* colr variation indices mapping during planning phase:
142
   * generate colrv1_variation_idx_delta_map. When delta set index map is not
143
   * included, it's a mapping from varIdx-> (new varIdx,delta). Otherwise, it's
144
   * a mapping from old delta set idx-> (new delta set idx, delta). Mapping
145
   * delta set indices is the same as gid mapping.
146
   * Besides, we need to generate a delta set idx-> new var_idx map for updating
147
   * delta set index map if exists. This map will be updated again after
148
   * instancing. */
149
36
  if (!plan->all_axes_pinned)
150
36
  {
151
36
    remap_variation_indices (var_store,
152
36
                              variation_indices,
153
36
                              plan->normalized_coords,
154
36
                              false, /* no need to calculate delta for COLR during planning */
155
36
                              plan->all_axes_pinned,
156
36
                              plan->colrv1_variation_idx_delta_map);
157
158
36
    if (colr.has_delta_set_index_map ())
159
36
      remap_colrv1_delta_set_index_indices (colr.get_delta_set_index_map (),
160
36
                                             delta_set_indices,
161
36
                                             plan->colrv1_variation_idx_delta_map,
162
36
                                             plan->colrv1_new_deltaset_idx_varidx_map);
163
36
  }
164
36
#endif
165
36
}
166
167
static inline void
168
_math_closure (hb_subset_plan_t *plan,
169
               hb_set_t         *glyphset)
170
35.6k
{
171
35.6k
  hb_blob_ptr_t<OT::MATH> math = plan->source_table<OT::MATH> ();
172
35.6k
  if (math->has_data ())
173
557
    math->closure_glyphs (glyphset);
174
35.6k
  math.destroy ();
175
35.6k
}
176
177
static inline void
178
_remove_invalid_gids (hb_set_t *glyphs,
179
          unsigned int num_glyphs)
180
142k
{
181
142k
  glyphs->del_range (num_glyphs, HB_SET_VALUE_INVALID);
182
142k
}
183
184
template<bool GID_ALWAYS_EXISTS = false, typename I, typename F, typename G, hb_requires (hb_is_iterator (I))>
185
static void
186
_fill_unicode_and_glyph_map(hb_subset_plan_t *plan,
187
                            I unicode_iterator,
188
                            F unicode_to_gid_for_iterator,
189
                            G unicode_to_gid_general)
190
50.6k
{
191
50.6k
  for (hb_codepoint_t cp : unicode_iterator)
192
236M
  {
193
236M
    hb_codepoint_t gid = unicode_to_gid_for_iterator(cp);
194
236M
    if (!GID_ALWAYS_EXISTS && gid == HB_MAP_VALUE_INVALID)
195
236M
    {
196
236M
      DEBUG_MSG(SUBSET, nullptr, "Drop U+%04X; no gid", cp);
197
236M
      continue;
198
236M
    }
199
200
64.4k
    plan->codepoint_to_glyph->set (cp, gid);
201
64.4k
    plan->unicode_to_new_gid_list.push (hb_pair (cp, gid));
202
64.4k
  }
203
50.6k
}
hb-subset-plan.cc:_ZL27_fill_unicode_and_glyph_mapILb0EN23hb_bit_set_invertible_t6iter_tEZL28_populate_unicodes_to_retainPK8hb_set_tS4_P16hb_subset_plan_tE3$_0S7_TnPN12hb_enable_ifIXsr17hb_is_iterator_ofIT0_NS9_6item_tEEE5valueEvE4typeELPv0EEvS6_S9_T1_T2_
Line
Count
Source
190
18.1k
{
191
18.1k
  for (hb_codepoint_t cp : unicode_iterator)
192
271k
  {
193
271k
    hb_codepoint_t gid = unicode_to_gid_for_iterator(cp);
194
271k
    if (!GID_ALWAYS_EXISTS && gid == HB_MAP_VALUE_INVALID)
195
221k
    {
196
221k
      DEBUG_MSG(SUBSET, nullptr, "Drop U+%04X; no gid", cp);
197
221k
      continue;
198
221k
    }
199
200
50.8k
    plan->codepoint_to_glyph->set (cp, gid);
201
50.8k
    plan->unicode_to_new_gid_list.push (hb_pair (cp, gid));
202
50.8k
  }
203
18.1k
}
Unexecuted instantiation: hb-subset-plan.cc:_ZL27_fill_unicode_and_glyph_mapILb0EN23hb_bit_set_invertible_t6iter_tEZL28_populate_unicodes_to_retainPK8hb_set_tS4_P16hb_subset_plan_tE3$_1S7_TnPN12hb_enable_ifIXsr17hb_is_iterator_ofIT0_NS9_6item_tEEE5valueEvE4typeELPv0EEvS6_S9_T1_T2_
Unexecuted instantiation: hb-subset-plan.cc:_ZL27_fill_unicode_and_glyph_mapILb1E10hb_array_tIKjEZL28_populate_unicodes_to_retainPK8hb_set_tS5_P16hb_subset_plan_tE3$_2ZL28_populate_unicodes_to_retainS5_S5_S7_E3$_3TnPN12hb_enable_ifIXsr17hb_is_iterator_ofIT0_NSB_6item_tEEE5valueEvE4typeELPv0EEvS7_SB_T1_T2_
Unexecuted instantiation: hb-subset-plan.cc:_ZL27_fill_unicode_and_glyph_mapILb0EN23hb_bit_set_invertible_t6iter_tEZL28_populate_unicodes_to_retainPK8hb_set_tS4_P16hb_subset_plan_tE3$_4ZL28_populate_unicodes_to_retainS4_S4_S6_E3$_5TnPN12hb_enable_ifIXsr17hb_is_iterator_ofIT0_NSA_6item_tEEE5valueEvE4typeELPv0EEvS6_SA_T1_T2_
hb-subset-plan.cc:_ZL27_fill_unicode_and_glyph_mapILb0E15hb_range_iter_tIjjEZL28_populate_unicodes_to_retainPK8hb_set_tS4_P16hb_subset_plan_tE3$_6ZL28_populate_unicodes_to_retainS4_S4_S6_E3$_7TnPN12hb_enable_ifIXsr17hb_is_iterator_ofIT0_NSA_6item_tEEE5valueEvE4typeELPv0EEvS6_SA_T1_T2_
Line
Count
Source
190
32.5k
{
191
32.5k
  for (hb_codepoint_t cp : unicode_iterator)
192
236M
  {
193
236M
    hb_codepoint_t gid = unicode_to_gid_for_iterator(cp);
194
236M
    if (!GID_ALWAYS_EXISTS && gid == HB_MAP_VALUE_INVALID)
195
236M
    {
196
236M
      DEBUG_MSG(SUBSET, nullptr, "Drop U+%04X; no gid", cp);
197
236M
      continue;
198
236M
    }
199
200
13.5k
    plan->codepoint_to_glyph->set (cp, gid);
201
13.5k
    plan->unicode_to_new_gid_list.push (hb_pair (cp, gid));
202
13.5k
  }
203
32.5k
}
204
205
template<bool GID_ALWAYS_EXISTS = false, typename I, typename F, hb_requires (hb_is_iterator (I))>
206
static void
207
_fill_unicode_and_glyph_map(hb_subset_plan_t *plan,
208
                            I unicode_iterator,
209
                            F unicode_to_gid_for_iterator)
210
18.1k
{
211
18.1k
  _fill_unicode_and_glyph_map(plan, unicode_iterator, unicode_to_gid_for_iterator, unicode_to_gid_for_iterator);
212
18.1k
}
hb-subset-plan.cc:_ZL27_fill_unicode_and_glyph_mapILb0EN23hb_bit_set_invertible_t6iter_tEZL28_populate_unicodes_to_retainPK8hb_set_tS4_P16hb_subset_plan_tE3$_0TnPN12hb_enable_ifIXsr17hb_is_iterator_ofIT0_NS9_6item_tEEE5valueEvE4typeELPv0EEvS6_S9_T1_
Line
Count
Source
210
18.1k
{
211
18.1k
  _fill_unicode_and_glyph_map(plan, unicode_iterator, unicode_to_gid_for_iterator, unicode_to_gid_for_iterator);
212
18.1k
}
Unexecuted instantiation: hb-subset-plan.cc:_ZL27_fill_unicode_and_glyph_mapILb0EN23hb_bit_set_invertible_t6iter_tEZL28_populate_unicodes_to_retainPK8hb_set_tS4_P16hb_subset_plan_tE3$_1TnPN12hb_enable_ifIXsr17hb_is_iterator_ofIT0_NS9_6item_tEEE5valueEvE4typeELPv0EEvS6_S9_T1_
213
214
/*
215
 * Finds additional unicode codepoints which are reachable from the input unicode set.
216
 * Currently this adds in mirrored variants (needed for bidi) of any input unicodes.
217
 */
218
static hb_set_t
219
35.6k
_unicode_closure (const hb_set_t* unicodes, bool bidi_closure) {
220
  // TODO: we may want to also consider pulling in reachable unicode composition and decompositions.
221
  //       see: https://github.com/harfbuzz/harfbuzz/issues/2283
222
35.6k
  hb_set_t out = *unicodes;
223
35.6k
  if (!bidi_closure) return out;
224
225
30.8k
  if (out.is_inverted()) {
226
    // don't closure inverted sets, they are asking to specifically exclude certain codepoints.
227
    // otherwise everything is already included.
228
0
    return out;
229
0
  }
230
231
30.8k
  auto unicode_funcs = hb_unicode_funcs_get_default ();
232
506k
  for (hb_codepoint_t cp : *unicodes) {
233
506k
   hb_codepoint_t mirror = hb_unicode_mirroring(unicode_funcs, cp);
234
506k
   if (unlikely (mirror != cp)) {
235
19.1k
     out.add(mirror);
236
19.1k
   }
237
506k
  }
238
239
30.8k
  return out;
240
30.8k
}
241
242
static void
243
_populate_unicodes_to_retain (const hb_set_t *unicodes_in,
244
                              const hb_set_t *glyphs,
245
                              hb_subset_plan_t *plan)
246
35.6k
{
247
35.6k
  hb_set_t unicodes = _unicode_closure(unicodes_in,
248
35.6k
    !(plan->flags & HB_SUBSET_FLAGS_NO_BIDI_CLOSURE));
249
250
35.6k
  OT::cmap::accelerator_t cmap (plan->source);
251
35.6k
  unsigned size_threshold = plan->source->get_num_glyphs ();
252
253
35.6k
  if (glyphs->is_empty () && unicodes.get_population () < size_threshold)
254
18.1k
  {
255
256
18.1k
    const hb_map_t* unicode_to_gid = nullptr;
257
18.1k
    if (plan->accelerator)
258
0
      unicode_to_gid = &plan->accelerator->unicode_to_gid;
259
260
    // This is approach to collection is faster, but can only be used  if glyphs
261
    // are not being explicitly added to the subset and the input unicodes set is
262
    // not excessively large (eg. an inverted set).
263
18.1k
    plan->unicode_to_new_gid_list.alloc (unicodes.get_population ());
264
18.1k
    if (!unicode_to_gid) {
265
271k
      _fill_unicode_and_glyph_map(plan, unicodes.iter(), [&] (hb_codepoint_t cp) {
266
271k
        hb_codepoint_t gid;
267
271k
        if (!cmap.get_nominal_glyph (cp, &gid)) {
268
220k
          return HB_MAP_VALUE_INVALID;
269
220k
        }
270
50.8k
        return gid;
271
271k
      });
272
18.1k
    } else {
273
      // Use in memory unicode to gid map it's faster then looking up from
274
      // the map. This code is mostly duplicated from above to avoid doing
275
      // conditionals on the presence of the unicode_to_gid map each
276
      // iteration.
277
0
      _fill_unicode_and_glyph_map(plan, unicodes.iter(), [&] (hb_codepoint_t cp) {
278
0
        return unicode_to_gid->get (cp);
279
0
      });
280
0
    }
281
18.1k
  }
282
17.4k
  else
283
17.4k
  {
284
    // This approach is slower, but can handle adding in glyphs to the subset and will match
285
    // them with cmap entries.
286
287
17.4k
    hb_map_t unicode_glyphid_map_storage;
288
17.4k
    hb_set_t cmap_unicodes_storage;
289
17.4k
    const hb_map_t* unicode_glyphid_map = &unicode_glyphid_map_storage;
290
17.4k
    const hb_set_t* cmap_unicodes = &cmap_unicodes_storage;
291
292
17.4k
    if (!plan->accelerator) {
293
17.4k
      cmap.collect_mapping (&cmap_unicodes_storage, &unicode_glyphid_map_storage);
294
17.4k
      plan->unicode_to_new_gid_list.alloc (hb_min(unicodes.get_population ()
295
17.4k
                                                  + glyphs->get_population (),
296
17.4k
                                                  cmap_unicodes->get_population ()));
297
17.4k
    } else {
298
0
      unicode_glyphid_map = &plan->accelerator->unicode_to_gid;
299
0
      cmap_unicodes = &plan->accelerator->unicodes;
300
0
    }
301
302
17.4k
    if (plan->accelerator &&
303
0
  unicodes.get_population () < cmap_unicodes->get_population () &&
304
0
  glyphs->get_population () < cmap_unicodes->get_population ())
305
0
    {
306
0
      plan->codepoint_to_glyph->alloc (unicodes.get_population () + glyphs->get_population ());
307
308
0
      auto &gid_to_unicodes = plan->accelerator->gid_to_unicodes;
309
310
0
      for (hb_codepoint_t gid : *glyphs)
311
0
      {
312
0
        auto unicodes = gid_to_unicodes.get (gid);
313
0
        _fill_unicode_and_glyph_map<true>(plan, unicodes, [&] (hb_codepoint_t cp) {
314
0
          return gid;
315
0
        },
316
0
        [&] (hb_codepoint_t cp) {
317
0
          return unicode_glyphid_map->get(cp);
318
0
        });
319
0
      }
320
321
0
      _fill_unicode_and_glyph_map(plan, unicodes.iter(), [&] (hb_codepoint_t cp) {
322
          /* Don't double-add entry. */
323
0
  if (plan->codepoint_to_glyph->has (cp))
324
0
          return HB_MAP_VALUE_INVALID;
325
326
0
        return unicode_glyphid_map->get(cp);
327
0
      },
328
0
      [&] (hb_codepoint_t cp) {
329
0
          return unicode_glyphid_map->get(cp);
330
0
      });
331
332
0
      plan->unicode_to_new_gid_list.qsort ();
333
0
    }
334
17.4k
    else
335
17.4k
    {
336
17.4k
      plan->codepoint_to_glyph->alloc (cmap_unicodes->get_population ());
337
17.4k
      hb_codepoint_t first = HB_SET_VALUE_INVALID, last = HB_SET_VALUE_INVALID;
338
49.9k
      for (; cmap_unicodes->next_range (&first, &last); )
339
32.5k
      {
340
236M
        _fill_unicode_and_glyph_map(plan, hb_range(first, last + 1), [&] (hb_codepoint_t cp) {
341
236M
          hb_codepoint_t gid = (*unicode_glyphid_map)[cp];
342
236M
    if (!unicodes.has (cp) && !glyphs->has (gid))
343
236M
      return HB_MAP_VALUE_INVALID;
344
13.8k
          return gid;
345
236M
        },
346
32.5k
        [&] (hb_codepoint_t cp) {
347
32.5k
          return unicode_glyphid_map->get(cp);
348
32.5k
        });
349
32.5k
      }
350
17.4k
    }
351
352
    /* Add gids which where requested, but not mapped in cmap */
353
17.4k
    unsigned num_glyphs = plan->source->get_num_glyphs ();
354
17.4k
    hb_codepoint_t first = HB_SET_VALUE_INVALID, last = HB_SET_VALUE_INVALID;
355
17.4k
    for (; glyphs->next_range (&first, &last); )
356
0
    {
357
0
      if (first >= num_glyphs)
358
0
  break;
359
0
      if (last >= num_glyphs)
360
0
        last = num_glyphs - 1;
361
0
      plan->_glyphset_gsub.add_range (first, last);
362
0
    }
363
17.4k
  }
364
365
35.6k
  auto &arr = plan->unicode_to_new_gid_list;
366
35.6k
  if (arr.length)
367
6.49k
  {
368
6.49k
    plan->unicodes.add_sorted_array (&arr.arrayZ->first, arr.length, sizeof (*arr.arrayZ));
369
6.49k
    plan->_glyphset_gsub.add_array (&arr.arrayZ->second, arr.length, sizeof (*arr.arrayZ));
370
6.49k
  }
371
372
  // Variation selectors don't have glyphs associated with them in the cmap so they will have been filtered out above
373
  // but should still be retained. Add them back here.
374
375
  // However, the min and max codepoints for OS/2 should be calculated without considering variation selectors,
376
  // so record those first.
377
35.6k
  plan->os2_info.min_cmap_codepoint = plan->unicodes.get_min();
378
35.6k
  plan->os2_info.max_cmap_codepoint = plan->unicodes.get_max();
379
380
35.6k
  hb_set_t variation_selectors_to_retain;
381
35.6k
  cmap.collect_variation_selectors(&variation_selectors_to_retain);
382
35.6k
  + variation_selectors_to_retain.iter()
383
35.6k
  | hb_filter(unicodes)
384
35.6k
  | hb_sink(&plan->unicodes)
385
35.6k
  ;
386
35.6k
}
387
388
static unsigned
389
_glyf_add_gid_and_children (const OT::glyf_accelerator_t &glyf,
390
          hb_codepoint_t gid,
391
          hb_set_t *gids_to_retain,
392
          int operation_count,
393
          unsigned depth = 0)
394
18.6M
{
395
  /* Check if is already visited */
396
18.6M
  if (gids_to_retain->has (gid)) return operation_count;
397
398
16.8M
  gids_to_retain->add (gid);
399
400
16.8M
  if (unlikely (depth++ > HB_MAX_NESTING_LEVEL)) return operation_count;
401
16.8M
  if (unlikely (--operation_count < 0)) return operation_count;
402
403
16.8M
  auto glyph = glyf.glyph_for_gid (gid);
404
405
16.8M
  for (auto &item : glyph.get_composite_iterator ())
406
3.49M
    operation_count =
407
3.49M
      _glyf_add_gid_and_children (glyf,
408
3.49M
          item.get_gid (),
409
3.49M
          gids_to_retain,
410
3.49M
          operation_count,
411
3.49M
          depth);
412
413
16.8M
  return operation_count;
414
16.8M
}
415
416
static void
417
_nameid_closure (hb_subset_plan_t* plan,
418
     hb_set_t* drop_tables)
419
35.6k
{
420
35.6k
#ifndef HB_NO_STYLE
421
35.6k
  if (!drop_tables->has (HB_OT_TAG_STAT))
422
35.6k
    plan->source->table.STAT->collect_name_ids (&plan->user_axes_location, &plan->name_ids);
423
35.6k
#endif
424
35.6k
#ifndef HB_NO_VAR
425
35.6k
  if (!plan->all_axes_pinned)
426
35.5k
    plan->source->table.fvar->collect_name_ids (&plan->user_axes_location, &plan->axes_old_index_tag_map, &plan->name_ids);
427
35.6k
#endif
428
35.6k
#ifndef HB_NO_COLOR
429
35.6k
  if (!drop_tables->has (HB_OT_TAG_CPAL))
430
35.6k
    plan->source->table.CPAL->collect_name_ids (&plan->colr_palettes, &plan->name_ids);
431
35.6k
#endif
432
433
35.6k
#ifndef HB_NO_SUBSET_LAYOUT
434
35.6k
  layout_nameid_closure(plan, drop_tables);
435
35.6k
#endif
436
35.6k
}
437
438
static void
439
_populate_gids_to_retain (hb_subset_plan_t* plan,
440
              hb_set_t* drop_tables)
441
35.6k
{
442
35.6k
  OT::glyf_accelerator_t glyf (plan->source);
443
35.6k
#ifndef HB_NO_SUBSET_CFF
444
  // Note: we cannot use inprogress_accelerator here, since it has not been
445
  // created yet. So in case of preprocessed-face (and otherwise), we do an
446
  // extra sanitize pass here, which is not ideal.
447
35.6k
  OT::cff1::accelerator_subset_t stack_cff (plan->accelerator ? nullptr : plan->source);
448
35.6k
  const OT::cff1::accelerator_subset_t *cff (plan->accelerator ? plan->accelerator->cff1_accel.get () : &stack_cff);
449
35.6k
#endif
450
451
35.6k
  plan->_glyphset_gsub.add (0); // Not-def
452
453
35.6k
  _cmap_closure (plan->source, &plan->unicodes, &plan->_glyphset_gsub);
454
455
35.6k
#ifndef HB_NO_SUBSET_LAYOUT
456
35.6k
  layout_populate_gids_to_retain(plan, drop_tables);
457
35.6k
#endif
458
459
35.6k
  _remove_invalid_gids (&plan->_glyphset_gsub, plan->source->get_num_glyphs ());
460
461
35.6k
  plan->_glyphset_mathed = plan->_glyphset_gsub;
462
35.6k
  if (!drop_tables->has (HB_OT_TAG_MATH))
463
35.6k
  {
464
35.6k
    _math_closure (plan, &plan->_glyphset_mathed);
465
35.6k
    _remove_invalid_gids (&plan->_glyphset_mathed, plan->source->get_num_glyphs ());
466
35.6k
  }
467
468
35.6k
  hb_set_t cur_glyphset = plan->_glyphset_mathed;
469
35.6k
  if (!drop_tables->has (HB_OT_TAG_COLR))
470
35.6k
  {
471
35.6k
    _colr_closure (plan, &cur_glyphset);
472
35.6k
    _remove_invalid_gids (&cur_glyphset, plan->source->get_num_glyphs ());
473
35.6k
  }
474
475
35.6k
  plan->_glyphset_colred = cur_glyphset;
476
477
  // XXX TODO VARC closure / subset
478
479
35.6k
  _nameid_closure (plan, drop_tables);
480
  /* Populate a full set of glyphs to retain by adding all referenced
481
   * composite glyphs. */
482
35.6k
  if (glyf.has_data ())
483
15.1k
    for (hb_codepoint_t gid : cur_glyphset)
484
15.1M
      _glyf_add_gid_and_children (glyf, gid, &plan->_glyphset,
485
15.1M
          cur_glyphset.get_population () * HB_MAX_COMPOSITE_OPERATIONS_PER_GLYPH);
486
20.4k
  else
487
20.4k
    plan->_glyphset.union_ (cur_glyphset);
488
35.6k
#ifndef HB_NO_SUBSET_CFF
489
35.6k
  if (!plan->accelerator || plan->accelerator->has_seac)
490
35.6k
  {
491
35.6k
    bool has_seac = false;
492
35.6k
    if (cff->is_valid ())
493
2.67k
      for (hb_codepoint_t gid : cur_glyphset)
494
7.83k
  if (_add_cff_seac_components (*cff, gid, &plan->_glyphset))
495
58
    has_seac = true;
496
35.6k
    plan->has_seac = has_seac;
497
35.6k
  }
498
35.6k
#endif
499
500
35.6k
  _remove_invalid_gids (&plan->_glyphset, plan->source->get_num_glyphs ());
501
502
35.6k
#ifndef HB_NO_VAR
503
35.6k
#ifndef HB_NO_SUBSET_LAYOUT
504
35.6k
  if (!drop_tables->has (HB_OT_TAG_GDEF))
505
35.6k
    collect_layout_variation_indices (plan);
506
35.6k
#endif
507
35.6k
#endif
508
35.6k
}
509
510
static void
511
_create_glyph_map_gsub (const hb_set_t* glyph_set_gsub,
512
                        const hb_map_t* glyph_map,
513
                        hb_map_t* out)
514
34.9k
{
515
34.9k
  out->alloc (glyph_set_gsub->get_population ());
516
34.9k
  + hb_iter (glyph_set_gsub)
517
15.7M
  | hb_map ([&] (hb_codepoint_t gid) {
518
15.7M
    return hb_codepoint_pair_t (gid, glyph_map->get (gid));
519
15.7M
  })
520
34.9k
  | hb_sink (out)
521
34.9k
  ;
522
34.9k
}
523
524
static bool
525
_create_old_gid_to_new_gid_map (const hb_face_t *face,
526
        bool     retain_gids,
527
        const hb_set_t  *all_gids_to_retain,
528
                                const hb_map_t  *requested_glyph_map,
529
        hb_map_t  *glyph_map, /* OUT */
530
        hb_map_t  *reverse_glyph_map, /* OUT */
531
        hb_sorted_vector_t<hb_codepoint_pair_t> *new_to_old_gid_list /* OUT */,
532
        unsigned int  *num_glyphs /* OUT */)
533
34.9k
{
534
34.9k
  unsigned pop = all_gids_to_retain->get_population ();
535
34.9k
  reverse_glyph_map->alloc (pop);
536
34.9k
  glyph_map->alloc (pop);
537
34.9k
  new_to_old_gid_list->alloc (pop);
538
539
34.9k
  if (*requested_glyph_map)
540
0
  {
541
0
    hb_set_t new_gids(requested_glyph_map->values());
542
0
    if (new_gids.get_population() != requested_glyph_map->get_population())
543
0
    {
544
0
      DEBUG_MSG (SUBSET, nullptr, "The provided custom glyph mapping is not unique.");
545
0
      return false;
546
0
    }
547
548
0
    if (retain_gids)
549
0
    {
550
0
      DEBUG_MSG (SUBSET, nullptr,
551
0
        "HB_SUBSET_FLAGS_RETAIN_GIDS cannot be set if "
552
0
        "a custom glyph mapping has been provided.");
553
0
      return false;
554
0
    }
555
556
0
    hb_codepoint_t max_glyph = 0;
557
0
    hb_set_t remaining;
558
0
    for (auto old_gid : all_gids_to_retain->iter ())
559
0
    {
560
0
      if (old_gid == 0) {
561
0
  new_to_old_gid_list->push (hb_pair<hb_codepoint_t, hb_codepoint_t> (0u, 0u));
562
0
        continue;
563
0
      }
564
565
0
      hb_codepoint_t* new_gid;
566
0
      if (!requested_glyph_map->has (old_gid, &new_gid))
567
0
      {
568
0
        remaining.add(old_gid);
569
0
        continue;
570
0
      }
571
572
0
      if (*new_gid > max_glyph)
573
0
        max_glyph = *new_gid;
574
0
      new_to_old_gid_list->push (hb_pair (*new_gid, old_gid));
575
0
    }
576
0
    new_to_old_gid_list->qsort ();
577
578
    // Anything that wasn't mapped by the requested mapping should
579
    // be placed after the requested mapping.
580
0
    for (auto old_gid : remaining)
581
0
      new_to_old_gid_list->push (hb_pair (++max_glyph, old_gid));
582
583
0
    *num_glyphs = max_glyph + 1;
584
0
  }
585
34.9k
  else if (!retain_gids)
586
29.2k
  {
587
29.2k
    + hb_enumerate (hb_iter (all_gids_to_retain), (hb_codepoint_t) 0)
588
29.2k
    | hb_sink (new_to_old_gid_list)
589
29.2k
    ;
590
29.2k
    *num_glyphs = new_to_old_gid_list->length;
591
29.2k
  }
592
5.71k
  else
593
5.71k
  {
594
5.71k
    + hb_iter (all_gids_to_retain)
595
4.08M
    | hb_map ([] (hb_codepoint_t _) {
596
4.08M
    return hb_codepoint_pair_t (_, _);
597
4.08M
        })
598
5.71k
    | hb_sink (new_to_old_gid_list)
599
5.71k
    ;
600
601
5.71k
    hb_codepoint_t max_glyph = HB_SET_VALUE_INVALID;
602
5.71k
    hb_set_previous (all_gids_to_retain, &max_glyph);
603
604
5.71k
    *num_glyphs = max_glyph + 1;
605
5.71k
  }
606
607
34.9k
  reverse_glyph_map->alloc (reverse_glyph_map->get_population () + new_to_old_gid_list->length);
608
34.9k
  + hb_iter (new_to_old_gid_list)
609
34.9k
  | hb_sink (reverse_glyph_map)
610
34.9k
  ;
611
34.9k
  glyph_map->alloc (glyph_map->get_population () + new_to_old_gid_list->length);
612
34.9k
  + hb_iter (new_to_old_gid_list)
613
34.9k
  | hb_map (&hb_codepoint_pair_t::reverse)
614
34.9k
  | hb_sink (glyph_map)
615
34.9k
  ;
616
617
34.9k
  return true;
618
34.9k
}
619
620
hb_subset_plan_t::hb_subset_plan_t (hb_face_t *face,
621
            const hb_subset_input_t *input)
622
35.6k
{
623
35.6k
  successful = true;
624
35.6k
  flags = input->flags;
625
626
35.6k
  unicode_to_new_gid_list.init ();
627
628
35.6k
  name_ids = *input->sets.name_ids;
629
35.6k
  name_languages = *input->sets.name_languages;
630
35.6k
  layout_features = *input->sets.layout_features;
631
35.6k
  layout_scripts = *input->sets.layout_scripts;
632
35.6k
  glyphs_requested = *input->sets.glyphs;
633
35.6k
  drop_tables = *input->sets.drop_tables;
634
35.6k
  no_subset_tables = *input->sets.no_subset_tables;
635
35.6k
  source = hb_face_reference (face);
636
35.6k
  dest = hb_face_builder_create ();
637
638
35.6k
  codepoint_to_glyph = hb_map_create ();
639
35.6k
  glyph_map = hb_map_create ();
640
35.6k
  reverse_glyph_map = hb_map_create ();
641
642
35.6k
  gsub_insert_catch_all_feature_variation_rec = false;
643
35.6k
  gpos_insert_catch_all_feature_variation_rec = false;
644
35.6k
  gdef_varstore_inner_maps.init ();
645
646
35.6k
  user_axes_location = input->axes_location;
647
35.6k
  all_axes_pinned = false;
648
35.6k
  pinned_at_default = true;
649
35.6k
  has_gdef_varstore = false;
650
651
35.6k
#ifdef HB_EXPERIMENTAL_API
652
35.6k
  for (auto _ : input->name_table_overrides)
653
0
  {
654
0
    hb_bytes_t name_bytes = _.second;
655
0
    unsigned len = name_bytes.length;
656
0
    char *name_str = (char *) hb_malloc (len);
657
0
    if (unlikely (!check_success (name_str)))
658
0
      break;
659
660
0
    hb_memcpy (name_str, name_bytes.arrayZ, len);
661
0
    name_table_overrides.set (_.first, hb_bytes_t (name_str, len));
662
0
  }
663
35.6k
#endif
664
665
35.6k
  void* accel = hb_face_get_user_data(face, hb_subset_accelerator_t::user_data_key());
666
667
35.6k
  attach_accelerator_data = input->attach_accelerator_data;
668
35.6k
  force_long_loca = input->force_long_loca;
669
35.6k
#ifdef HB_EXPERIMENTAL_API
670
35.6k
  force_long_loca = force_long_loca || (flags & HB_SUBSET_FLAGS_IFTB_REQUIREMENTS);
671
35.6k
#endif
672
673
35.6k
  if (accel)
674
0
    accelerator = (hb_subset_accelerator_t*) accel;
675
676
35.6k
  if (unlikely (in_error ()))
677
0
    return;
678
679
35.6k
#ifndef HB_NO_VAR
680
35.6k
  if (!check_success (normalize_axes_location (face, this)))
681
2
      return;
682
35.6k
#endif
683
684
35.6k
  _populate_unicodes_to_retain (input->sets.unicodes, input->sets.glyphs, this);
685
686
35.6k
  _populate_gids_to_retain (this, input->sets.drop_tables);
687
35.6k
  if (unlikely (in_error ()))
688
658
    return;
689
690
34.9k
  if (!check_success(_create_old_gid_to_new_gid_map(
691
34.9k
          face,
692
34.9k
          input->flags & HB_SUBSET_FLAGS_RETAIN_GIDS,
693
34.9k
          &_glyphset,
694
34.9k
          &input->glyph_map,
695
34.9k
          glyph_map,
696
34.9k
          reverse_glyph_map,
697
34.9k
    &new_to_old_gid_list,
698
34.9k
          &_num_output_glyphs))) {
699
0
    return;
700
0
  }
701
702
34.9k
#ifdef HB_EXPERIMENTAL_API  
703
34.9k
  if ((input->flags & HB_SUBSET_FLAGS_RETAIN_GIDS) &&
704
5.71k
      (input->flags & HB_SUBSET_FLAGS_RETAIN_NUM_GLYPHS)) {
705
    // We've been requested to maintain the num glyphs count from the
706
    // input face.
707
2.73k
    _num_output_glyphs = source->get_num_glyphs ();
708
2.73k
  }
709
34.9k
#endif
710
711
34.9k
  _create_glyph_map_gsub (
712
34.9k
      &_glyphset_gsub,
713
34.9k
      glyph_map,
714
34.9k
      &glyph_map_gsub);
715
716
  // Now that we have old to new gid map update the unicode to new gid list.
717
98.9k
  for (unsigned i = 0; i < unicode_to_new_gid_list.length; i++)
718
64.0k
  {
719
    // Use raw array access for performance.
720
64.0k
    unicode_to_new_gid_list.arrayZ[i].second =
721
64.0k
        glyph_map->get(unicode_to_new_gid_list.arrayZ[i].second);
722
64.0k
  }
723
724
34.9k
  bounds_width_vec.resize_dirty  (_num_output_glyphs);
725
34.9k
  for (auto &v : bounds_width_vec)
726
95.8M
    v = 0xFFFFFFFF;
727
34.9k
  bounds_height_vec.resize_dirty  (_num_output_glyphs);
728
34.9k
  for (auto &v : bounds_height_vec)
729
112M
    v = 0xFFFFFFFF;
730
731
34.9k
#ifndef HB_NO_SUBSET_LAYOUT    
732
34.9k
  if (!drop_tables.has (HB_OT_TAG_GDEF))
733
34.9k
    remap_used_mark_sets (this, used_mark_sets_map);
734
34.9k
#endif
735
736
34.9k
#ifndef HB_NO_VAR
737
34.9k
#ifndef HB_NO_BASE
738
34.9k
  if (!drop_tables.has (HB_OT_TAG_BASE))
739
34.9k
    collect_base_variation_indices (this);
740
34.9k
#endif
741
34.9k
#endif
742
743
34.9k
  if (unlikely (in_error ()))
744
0
    return;
745
746
34.9k
#ifndef HB_NO_VAR
747
34.9k
  update_instance_metrics_map_from_cff2 (this);
748
34.9k
  if (!check_success (get_instance_glyphs_contour_points (this)))
749
66
      return;
750
34.8k
#endif
751
752
34.8k
  if (attach_accelerator_data)
753
0
  {
754
0
    inprogress_accelerator =
755
0
      hb_subset_accelerator_t::create (source,
756
0
               *codepoint_to_glyph,
757
0
                                       unicodes,
758
0
               has_seac);
759
760
0
    check_success (inprogress_accelerator);
761
0
  }
762
763
1.95M
#define HB_SUBSET_PLAN_MEMBER(Type, Name) check_success (!Name.in_error ());
764
34.8k
#include "hb-subset-plan-member-list.hh"
765
34.8k
#undef HB_SUBSET_PLAN_MEMBER
766
34.8k
}
767
768
hb_subset_plan_t::~hb_subset_plan_t()
769
35.6k
{
770
35.6k
  hb_face_destroy (dest);
771
772
35.6k
  hb_map_destroy (codepoint_to_glyph);
773
35.6k
  hb_map_destroy (glyph_map);
774
35.6k
  hb_map_destroy (reverse_glyph_map);
775
35.6k
#ifndef HB_NO_SUBSET_CFF
776
35.6k
  cff1_accel.fini ();
777
35.6k
  cff2_accel.fini ();
778
35.6k
#endif
779
35.6k
  hb_face_destroy (source);
780
781
35.6k
#ifdef HB_EXPERIMENTAL_API
782
35.6k
  for (auto _ : name_table_overrides.iter_ref ())
783
0
    _.second.fini ();
784
35.6k
#endif
785
786
35.6k
  if (inprogress_accelerator)
787
0
    hb_subset_accelerator_t::destroy ((void*) inprogress_accelerator);
788
35.6k
}
789
790
791
/**
792
 * hb_subset_plan_create_or_fail:
793
 * @face: font face to create the plan for.
794
 * @input: a #hb_subset_input_t input.
795
 *
796
 * Computes a plan for subsetting the supplied face according
797
 * to a provided input. The plan describes
798
 * which tables and glyphs should be retained.
799
 *
800
 * Return value: (transfer full): New subset plan. Destroy with
801
 * hb_subset_plan_destroy(). If there is a failure creating the plan
802
 * nullptr will be returned.
803
 *
804
 * Since: 4.0.0
805
 **/
806
hb_subset_plan_t *
807
hb_subset_plan_create_or_fail (hb_face_t   *face,
808
                               const hb_subset_input_t *input)
809
35.6k
{
810
35.6k
  hb_subset_plan_t *plan;
811
35.6k
  if (unlikely (!(plan = hb_object_create<hb_subset_plan_t> (face, input))))
812
36
    return nullptr;
813
814
35.6k
  if (unlikely (plan->in_error ()))
815
2.79k
  {
816
2.79k
    hb_subset_plan_destroy (plan);
817
2.79k
    return nullptr;
818
2.79k
  }
819
820
32.8k
  return plan;
821
35.6k
}
822
823
/**
824
 * hb_subset_plan_destroy:
825
 * @plan: a #hb_subset_plan_t
826
 *
827
 * Decreases the reference count on @plan, and if it reaches zero, destroys
828
 * @plan, freeing all memory.
829
 *
830
 * Since: 4.0.0
831
 **/
832
void
833
hb_subset_plan_destroy (hb_subset_plan_t *plan)
834
35.6k
{
835
35.6k
  if (!hb_object_destroy (plan)) return;
836
837
35.6k
  hb_free (plan);
838
35.6k
}
839
840
/**
841
 * hb_subset_plan_old_to_new_glyph_mapping:
842
 * @plan: a subsetting plan.
843
 *
844
 * Returns the mapping between glyphs in the original font to glyphs in the
845
 * subset that will be produced by @plan
846
 *
847
 * Return value: (transfer none):
848
 * A pointer to the #hb_map_t of the mapping.
849
 *
850
 * Since: 4.0.0
851
 **/
852
hb_map_t *
853
hb_subset_plan_old_to_new_glyph_mapping (const hb_subset_plan_t *plan)
854
0
{
855
0
  return plan->glyph_map;
856
0
}
857
858
/**
859
 * hb_subset_plan_new_to_old_glyph_mapping:
860
 * @plan: a subsetting plan.
861
 *
862
 * Returns the mapping between glyphs in the subset that will be produced by
863
 * @plan and the glyph in the original font.
864
 *
865
 * Return value: (transfer none):
866
 * A pointer to the #hb_map_t of the mapping.
867
 *
868
 * Since: 4.0.0
869
 **/
870
hb_map_t *
871
hb_subset_plan_new_to_old_glyph_mapping (const hb_subset_plan_t *plan)
872
0
{
873
0
  return plan->reverse_glyph_map;
874
0
}
875
876
/**
877
 * hb_subset_plan_unicode_to_old_glyph_mapping:
878
 * @plan: a subsetting plan.
879
 *
880
 * Returns the mapping between codepoints in the original font and the
881
 * associated glyph id in the original font.
882
 *
883
 * Return value: (transfer none):
884
 * A pointer to the #hb_map_t of the mapping.
885
 *
886
 * Since: 4.0.0
887
 **/
888
hb_map_t *
889
hb_subset_plan_unicode_to_old_glyph_mapping (const hb_subset_plan_t *plan)
890
0
{
891
0
  return plan->codepoint_to_glyph;
892
0
}
893
894
/**
895
 * hb_subset_plan_reference: (skip)
896
 * @plan: a #hb_subset_plan_t object.
897
 *
898
 * Increases the reference count on @plan.
899
 *
900
 * Return value: @plan.
901
 *
902
 * Since: 4.0.0
903
 **/
904
hb_subset_plan_t *
905
hb_subset_plan_reference (hb_subset_plan_t *plan)
906
0
{
907
0
  return hb_object_reference (plan);
908
0
}
909
910
/**
911
 * hb_subset_plan_set_user_data: (skip)
912
 * @plan: a #hb_subset_plan_t object.
913
 * @key: The user-data key to set
914
 * @data: A pointer to the user data
915
 * @destroy: (nullable): A callback to call when @data is not needed anymore
916
 * @replace: Whether to replace an existing data with the same key
917
 *
918
 * Attaches a user-data key/data pair to the given subset plan object.
919
 *
920
 * Return value: `true` if success, `false` otherwise
921
 *
922
 * Since: 4.0.0
923
 **/
924
hb_bool_t
925
hb_subset_plan_set_user_data (hb_subset_plan_t   *plan,
926
                              hb_user_data_key_t *key,
927
                              void               *data,
928
                              hb_destroy_func_t   destroy,
929
                              hb_bool_t           replace)
930
0
{
931
0
  return hb_object_set_user_data (plan, key, data, destroy, replace);
932
0
}
933
934
/**
935
 * hb_subset_plan_get_user_data: (skip)
936
 * @plan: a #hb_subset_plan_t object.
937
 * @key: The user-data key to query
938
 *
939
 * Fetches the user data associated with the specified key,
940
 * attached to the specified subset plan object.
941
 *
942
 * Return value: (transfer none): A pointer to the user data
943
 *
944
 * Since: 4.0.0
945
 **/
946
void *
947
hb_subset_plan_get_user_data (const hb_subset_plan_t *plan,
948
                              hb_user_data_key_t     *key)
949
0
{
950
0
  return hb_object_get_user_data (plan, key);
951
0
}