Coverage Report

Created: 2026-11-10 07:41

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/harfbuzz/src/hb-subset-plan-layout.cc
Line
Count
Source
1
/*
2
 * Copyright © 2023  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, Qunxin Liu, Roderick Sheeter
25
 */
26
27
#include "hb-subset-plan.hh"
28
29
#include "hb-ot-layout-gdef-table.hh"
30
#include "hb-ot-layout-gpos-table.hh"
31
#include "hb-ot-layout-gsub-table.hh"
32
#include "hb-ot-math-table.hh"
33
34
using OT::Layout::GSUB;
35
using OT::Layout::GPOS;
36
37
#ifndef HB_NO_SUBSET_LAYOUT
38
39
double
40
hb_subset_plan_t::layout_variation_delta (unsigned var_idx HB_UNUSED)
41
0
{
42
0
#ifndef HB_NO_VAR
43
0
  hb_blob_ptr_t<OT::GDEF> gdef = source_table<OT::GDEF> ();
44
0
  double delta = gdef->get_var_store ().get_delta<double> (var_idx,
45
0
                                                normalized_coords.as_array ());
46
0
  return delta;
47
#else
48
  return 0.f;
49
#endif
50
0
}
51
52
void
53
remap_used_mark_sets (hb_subset_plan_t *plan,
54
                      hb_map_t& used_mark_sets_map)
55
0
{
56
0
  hb_blob_ptr_t<OT::GDEF> gdef = plan->source_table<OT::GDEF> ();
57
58
0
  if (!gdef->has_data () || !gdef->has_mark_glyph_sets ())
59
0
  {
60
0
    return;
61
0
  }
62
63
0
  hb_set_t used_mark_sets;
64
0
  gdef->get_mark_glyph_sets ().collect_used_mark_sets (plan->_glyphset_gsub, used_mark_sets);
65
66
0
  remap_indexes (&used_mark_sets, &used_mark_sets_map);
67
0
}
68
69
/*
70
 * Removes all tags from 'tags' that are not in filter. Additionally eliminates any duplicates.
71
 * Returns true if anything was removed (not including duplicates).
72
 */
73
static bool _filter_tag_list(hb_vector_t<hb_tag_t>* tags, /* IN/OUT */
74
                             const hb_set_t* filter)
75
0
{
76
0
  hb_vector_t<hb_tag_t> out;
77
0
  out.alloc (tags->get_size() + 1); // +1 is to allocate room for the null terminator.
78
79
0
  bool removed = false;
80
0
  hb_set_t visited;
81
82
0
  for (hb_tag_t tag : *tags)
83
0
  {
84
0
    if (!tag) continue;
85
0
    if (visited.has (tag)) continue;
86
87
0
    if (!filter->has (tag))
88
0
    {
89
0
      removed = true;
90
0
      continue;
91
0
    }
92
93
0
    visited.add (tag);
94
0
    out.push (tag);
95
0
  }
96
97
  // The collect function needs a null element to signal end of the array.
98
0
  out.push (HB_TAG_NONE);
99
100
0
  hb_swap (out, *tags);
101
0
  return removed;
102
0
}
103
104
template <typename T>
105
static bool
106
_collect_instanced_lookup_variations (
107
    hb_subset_plan_t *plan,
108
    const T& table,
109
    const hb_set_t *feature_indices,
110
    hb_hashmap_t<unsigned, hb::shared_ptr<hb_set_t>> *lookup_variations)
111
0
{
112
0
  hb_blob_ptr_t<OT::GDEF> gdef = plan->source_table<OT::GDEF> ();
113
0
  OT::ItemVarStoreInstancer instancer (&gdef->get_var_store (), nullptr,
114
0
               plan->normalized_coords.as_array ());
115
0
  hb_vector_t<unsigned> state;
116
0
  unsigned variations_index;
117
0
  bool success = table.get_feature_variations_state (
118
0
  plan->normalized_coords.arrayZ,
119
0
  plan->normalized_coords.length,
120
0
  &variations_index,
121
0
  &instancer,
122
0
  &state);
123
124
0
  if (unlikely (!success || state.in_error ()))
125
0
    return plan->check_success (false);
126
127
0
  const auto &variations = table.get_feature_variations ();
128
0
  for (const auto &record : variations.get_lookup_variation_records ())
129
0
  {
130
0
    unsigned feature_index = record.get_feature_index ();
131
0
    if (!feature_indices->has (feature_index)) continue;
132
133
0
    hb::shared_ptr<hb_set_t> lookups {hb_set_create ()};
134
0
    record.collect_lookups (&variations,
135
0
                           table.get_feature_variation (feature_index, variations_index),
136
0
                           state.as_sorted_array (), lookups);
137
138
0
    if (unlikely (lookups->in_error () ||
139
0
      !lookup_variations->set (feature_index, lookups)))
140
0
      return plan->check_success (false);
141
0
  }
142
143
0
  return true;
144
0
}
Unexecuted instantiation: hb-subset-plan-layout.cc:bool _collect_instanced_lookup_variations<OT::Layout::GSUB>(hb_subset_plan_t*, OT::Layout::GSUB const&, hb_set_t const*, hb_hashmap_t<unsigned int, hb::shared_ptr<hb_set_t>, false>*)
Unexecuted instantiation: hb-subset-plan-layout.cc:bool _collect_instanced_lookup_variations<OT::Layout::GPOS>(hb_subset_plan_t*, OT::Layout::GPOS const&, hb_set_t const*, hb_hashmap_t<unsigned int, hb::shared_ptr<hb_set_t>, false>*)
145
146
template <typename T>
147
static void _collect_layout_indices (hb_subset_plan_t     *plan,
148
                                     const T&              table,
149
                                     hb_set_t     *lookup_indices, /* OUT */
150
                                     hb_set_t     *feature_indices, /* OUT */
151
                                     hb_hashmap_t<unsigned, hb::shared_ptr<hb_set_t>> *feature_record_cond_idx_map, /* OUT */
152
                                     hb_hashmap_t<unsigned, const OT::Feature*> *feature_substitutes_map, /* OUT */
153
                                     hb_hashmap_t<unsigned, hb::shared_ptr<hb_set_t>> *lookup_variations, /* OUT */
154
                                     hb_set_t& catch_all_record_feature_idxes, /* OUT */
155
                                     hb_hashmap_t<unsigned, hb_pair_t<const void*, const void*>>& catch_all_record_idx_feature_map /* OUT */)
156
0
{
157
0
  unsigned num_features = table.get_feature_count ();
158
0
  hb_vector_t<hb_tag_t> features;
159
0
  if (!plan->check_success (features.resize (num_features))) return;
160
0
  table.get_feature_tags (0, &num_features, features.arrayZ);
161
0
  bool retain_all_features = !_filter_tag_list (&features, &plan->layout_features);
162
163
0
  unsigned num_scripts = table.get_script_count ();
164
0
  hb_vector_t<hb_tag_t> scripts;
165
0
  if (!plan->check_success (scripts.resize (num_scripts))) return;
166
0
  table.get_script_tags (0, &num_scripts, scripts.arrayZ);
167
0
  bool retain_all_scripts = !_filter_tag_list (&scripts, &plan->layout_scripts);
168
169
0
  if (!plan->check_success (!features.in_error ()) || !features
170
0
      || !plan->check_success (!scripts.in_error ()) || !scripts)
171
0
    return;
172
173
0
  hb_ot_layout_collect_features (plan->source,
174
0
                                 T::tableTag,
175
0
                                 retain_all_scripts ? nullptr : scripts.arrayZ,
176
0
                                 nullptr,
177
0
                                 retain_all_features ? nullptr : features.arrayZ,
178
0
                                 feature_indices);
179
180
0
#ifndef HB_NO_VAR
181
  // collect feature substitutes with variations
182
0
  if (!plan->user_axes_location.is_empty ())
183
0
  {
184
0
    hb_blob_ptr_t<OT::GDEF> gdef = plan->source_table<OT::GDEF> ();
185
0
    OT::ItemVarStoreInstancer instancer (&gdef->get_var_store (), nullptr,
186
0
           plan->normalized_coords.as_array ());
187
0
    hb_hashmap_t<hb::shared_ptr<hb_map_t>, unsigned> conditionset_map;
188
0
    OT::hb_collect_feature_substitutes_with_var_context_t c =
189
0
    {
190
0
      &plan->axes_old_index_tag_map,
191
0
      &plan->axes_location,
192
0
      feature_record_cond_idx_map,
193
0
      feature_substitutes_map,
194
0
      catch_all_record_feature_idxes,
195
0
      feature_indices,
196
0
      false,
197
0
      false,
198
0
      false,
199
0
      0,
200
0
      &conditionset_map,
201
0
      &instancer,
202
0
      plan->all_axes_pinned
203
0
    };
204
0
    table.collect_feature_substitutes_with_variations (&c);
205
0
  }
206
0
#endif
207
208
0
#ifndef HB_NO_VAR
209
0
  if (plan->all_axes_pinned &&
210
0
      !_collect_instanced_lookup_variations (plan, table, feature_indices, lookup_variations))
211
0
    return;
212
0
#endif
213
214
0
  for (unsigned feature_index : *feature_indices)
215
0
  {
216
0
    hb::shared_ptr<hb_set_t> *lookups;
217
0
    if (lookup_variations->has (feature_index, &lookups))
218
0
    {
219
0
      lookup_indices->union_ (**lookups);
220
0
      continue;
221
0
    }
222
223
0
    const OT::Feature* f = &(table.get_feature (feature_index));
224
0
    const OT::Feature **p = nullptr;
225
0
    if (feature_substitutes_map->has (feature_index, &p))
226
0
      f = *p;
227
228
0
    f->add_lookup_indexes_to (lookup_indices);
229
0
  }
230
231
0
#ifndef HB_NO_VAR
232
0
  if (catch_all_record_feature_idxes)
233
0
  {
234
0
    for (unsigned feature_index : catch_all_record_feature_idxes)
235
0
    {
236
0
      const OT::Feature& f = table.get_feature (feature_index);
237
0
      f.add_lookup_indexes_to (lookup_indices);
238
0
      const void *tag = reinterpret_cast<const void*> (&(table.get_feature_list ().get_tag (feature_index)));
239
0
      catch_all_record_idx_feature_map.set (feature_index, hb_pair (&f, tag));
240
0
    }
241
0
  }
242
243
  // If all axes are pinned then all feature variations will be dropped so there's no need
244
  // to collect lookups from them.
245
0
  if (!plan->all_axes_pinned)
246
0
    table.feature_variation_collect_lookups (feature_indices,
247
0
                                             plan->user_axes_location.is_empty () ? nullptr: feature_record_cond_idx_map,
248
0
                                             lookup_indices);
249
0
#endif
250
0
}
Unexecuted instantiation: hb-subset-plan-layout.cc:void _collect_layout_indices<OT::Layout::GSUB>(hb_subset_plan_t*, OT::Layout::GSUB const&, hb_set_t*, hb_set_t*, hb_hashmap_t<unsigned int, hb::shared_ptr<hb_set_t>, false>*, hb_hashmap_t<unsigned int, OT::Feature const*, false>*, hb_hashmap_t<unsigned int, hb::shared_ptr<hb_set_t>, false>*, hb_set_t&, hb_hashmap_t<unsigned int, hb_pair_t<void const*, void const*>, false>&)
Unexecuted instantiation: hb-subset-plan-layout.cc:void _collect_layout_indices<OT::Layout::GPOS>(hb_subset_plan_t*, OT::Layout::GPOS const&, hb_set_t*, hb_set_t*, hb_hashmap_t<unsigned int, hb::shared_ptr<hb_set_t>, false>*, hb_hashmap_t<unsigned int, OT::Feature const*, false>*, hb_hashmap_t<unsigned int, hb::shared_ptr<hb_set_t>, false>*, hb_set_t&, hb_hashmap_t<unsigned int, hb_pair_t<void const*, void const*>, false>&)
251
252
253
static bool
254
_features_have_equal_params (const OT::Feature &a HB_UNUSED,
255
                            const OT::Feature &b HB_UNUSED,
256
                            hb_tag_t tag HB_UNUSED)
257
0
{
258
0
#ifndef HB_NO_LAYOUT_FEATURE_PARAMS
259
0
  if (bool (a.featureParams) != bool (b.featureParams)) return false;
260
0
  if (!a.featureParams) return true;
261
0
  const OT::FeatureParams &ap = a.get_feature_params ();
262
0
  const OT::FeatureParams &bp = b.get_feature_params ();
263
0
  if (tag == HB_TAG ('s','i','z','e'))
264
0
    return !hb_memcmp (&ap.get_size_params (tag), &bp.get_size_params (tag),
265
0
                       OT::FeatureParamsSize::static_size);
266
0
  if ((tag & 0xFFFF0000u) == HB_TAG ('s','s','\0','\0'))
267
0
    return !hb_memcmp (&ap.get_stylistic_set_params (tag), &bp.get_stylistic_set_params (tag),
268
0
                       OT::FeatureParamsStylisticSet::static_size);
269
0
  if ((tag & 0xFFFF0000u) == HB_TAG ('c','v','\0','\0'))
270
0
  {
271
0
    const auto &ac = ap.get_character_variants_params (tag);
272
0
    const auto &bc = bp.get_character_variants_params (tag);
273
0
    return ac.get_size () == bc.get_size () &&
274
0
           !hb_memcmp (&ac, &bc, ac.get_size ());
275
0
  }
276
0
#endif
277
0
  return true;
278
0
}
279
280
static inline void
281
_GSUBGPOS_find_duplicate_features (const OT::GSUBGPOS &g,
282
           const hb_map_t *lookup_indices,
283
           const hb_set_t *feature_indices,
284
           const hb_hashmap_t<unsigned, const OT::Feature*> *feature_substitutes_map,
285
                                   bool all_axes_pinned HB_UNUSED,
286
           hb_map_t *duplicate_feature_map /* OUT */)
287
0
{
288
0
  if (feature_indices->is_empty ()) return;
289
0
#ifndef HB_NO_VAR
290
0
  hb_set_t variable_features;
291
0
  if (!all_axes_pinned)
292
0
    g.get_feature_variations ().collect_feature_indices (&variable_features);
293
  /* Lookup variations are instanced, not dropped, when all axes are pinned. */
294
0
  g.get_feature_variations ().collect_lookup_variation_feature_indices (&variable_features);
295
0
  if (unlikely (variable_features.in_error ())) return;
296
0
#endif
297
0
  hb_hashmap_t<hb_tag_t, hb::unique_ptr<hb_set_t>> unique_features;
298
  //find out duplicate features after subset
299
0
  for (unsigned i : feature_indices->iter ())
300
0
  {
301
0
    hb_tag_t t = g.get_feature_tag (i);
302
0
    if (t == HB_MAP_VALUE_INVALID) continue;
303
0
    if (!unique_features.has (t))
304
0
    {
305
0
      if (unlikely (!unique_features.set (t, hb::unique_ptr<hb_set_t> {hb_set_create ()})))
306
0
  return;
307
0
      if (unique_features.has (t))
308
0
  unique_features.get (t)->add (i);
309
0
      duplicate_feature_map->set (i, i);
310
0
      continue;
311
0
    }
312
313
0
    bool found = false;
314
315
0
    hb_set_t* same_tag_features = unique_features.get (t);
316
0
    for (unsigned other_f_index : same_tag_features->iter ())
317
0
    {
318
0
#ifndef HB_NO_VAR
319
      /* Matching base lookups do not imply matching alternate behavior. */
320
0
      if (variable_features.has (i) || variable_features.has (other_f_index))
321
0
  continue;
322
0
#endif
323
324
0
      const OT::Feature* f = &(g.get_feature (i));
325
0
      const OT::Feature **p = nullptr;
326
0
      if (feature_substitutes_map->has (i, &p))
327
0
        f = *p;
328
329
0
      const OT::Feature* other_f = &(g.get_feature (other_f_index));
330
0
      if (feature_substitutes_map->has (other_f_index, &p))
331
0
        other_f = *p;
332
333
0
      if (!_features_have_equal_params (*f, *other_f, t)) continue;
334
335
0
      auto f_iter =
336
0
      + hb_iter (f->lookupIndex)
337
0
      | hb_filter (lookup_indices)
338
0
      ;
339
340
0
      auto other_f_iter =
341
0
      + hb_iter (other_f->lookupIndex)
342
0
      | hb_filter (lookup_indices)
343
0
      ;
344
345
0
      bool is_equal = true;
346
0
      for (; f_iter && other_f_iter; f_iter++, other_f_iter++)
347
0
      {
348
0
  unsigned a = *f_iter;
349
0
  unsigned b = *other_f_iter;
350
0
  if (a != b) { is_equal = false; break; }
351
0
      }
352
353
0
      if (is_equal == false || f_iter || other_f_iter) continue;
354
355
0
      found = true;
356
0
      duplicate_feature_map->set (i, other_f_index);
357
0
      break;
358
0
    }
359
360
0
    if (found == false)
361
0
    {
362
0
      same_tag_features->add (i);
363
0
      duplicate_feature_map->set (i, i);
364
0
    }
365
0
  }
366
0
}
367
368
static void
369
remap_feature_indices (const hb_set_t &feature_indices,
370
                       const hb_map_t &duplicate_feature_map,
371
                       const hb_hashmap_t<unsigned, hb_pair_t<const void*, const void*>>& catch_all_record_idx_feature_map,
372
                       hb_map_t       *mapping, /* OUT */
373
                       hb_map_t       *mapping_w_duplicates /* OUT */)
374
0
{
375
0
  unsigned i = 0;
376
0
  for (const auto _ : feature_indices)
377
0
  {
378
    // retain those features in case we need to insert a catch-all record to reinstate the old features
379
0
    if (catch_all_record_idx_feature_map.has (_))
380
0
    {
381
0
      mapping->set (_, i);
382
0
      mapping_w_duplicates->set (_, i);
383
0
      i++;
384
0
    }
385
0
    else
386
0
    {
387
0
      uint32_t f_idx = duplicate_feature_map.get (_);
388
0
      uint32_t *new_idx;
389
0
      if (mapping-> has (f_idx, &new_idx))
390
0
      {
391
0
        mapping_w_duplicates->set (_, *new_idx);
392
0
      }
393
0
      else
394
0
      {
395
0
        mapping->set (_, i);
396
0
        mapping_w_duplicates->set (_, i);
397
0
        i++;
398
0
      }
399
0
    }
400
0
  }
401
0
}
402
403
template <typename T>
404
static void
405
_closure_glyphs_lookups_features (hb_subset_plan_t   *plan,
406
         hb_set_t      *gids_to_retain,
407
         hb_map_t      *lookups,
408
         hb_map_t      *features,
409
         hb_map_t      *features_w_duplicates,
410
         script_langsys_map *langsys_map,
411
         hb_hashmap_t<unsigned, hb::shared_ptr<hb_set_t>> *feature_record_cond_idx_map,
412
         hb_hashmap_t<unsigned, const OT::Feature*> *feature_substitutes_map,
413
                                 hb_hashmap_t<unsigned, hb::shared_ptr<hb_set_t>> *lookup_variations,
414
                                 hb_set_t &catch_all_record_feature_idxes,
415
                                 hb_hashmap_t<unsigned, hb_pair_t<const void*, const void*>>& catch_all_record_idx_feature_map)
416
0
{
417
0
  hb_blob_ptr_t<T> table = plan->source_table<T> ();
418
0
  hb_tag_t table_tag = table->tableTag;
419
0
  hb_set_t lookup_indices, feature_indices;
420
0
  _collect_layout_indices<T> (plan,
421
0
                              *table,
422
0
                              &lookup_indices,
423
0
                              &feature_indices,
424
0
                              feature_record_cond_idx_map,
425
0
                              feature_substitutes_map,
426
0
                              lookup_variations,
427
0
                              catch_all_record_feature_idxes,
428
0
                              catch_all_record_idx_feature_map);
429
430
0
  if (table_tag == HB_OT_TAG_GSUB && !(plan->flags & HB_SUBSET_FLAGS_NO_LAYOUT_CLOSURE))
431
0
    hb_ot_layout_lookups_substitute_closure (plan->source,
432
0
                                             &lookup_indices,
433
0
               gids_to_retain);
434
0
  table->closure_lookups (plan->source,
435
0
        gids_to_retain,
436
0
                          &lookup_indices);
437
0
  remap_indexes (&lookup_indices, lookups);
438
439
  // prune features
440
0
  table->prune_features (lookups,
441
0
                         plan->user_axes_location.is_empty () ? nullptr : feature_record_cond_idx_map,
442
0
                         feature_substitutes_map,
443
0
                         &feature_indices);
444
0
  hb_map_t duplicate_feature_map;
445
0
  _GSUBGPOS_find_duplicate_features (*table, lookups, &feature_indices, feature_substitutes_map,
446
0
                                    plan->all_axes_pinned, &duplicate_feature_map);
447
448
0
  feature_indices.clear ();
449
0
  table->prune_langsys (&duplicate_feature_map, &plan->layout_scripts, langsys_map, &feature_indices);
450
0
  remap_feature_indices (feature_indices, duplicate_feature_map, catch_all_record_idx_feature_map, features, features_w_duplicates);
451
452
0
#ifndef HB_NO_VAR
453
  // Populate alternate parameter tags after deduplication. Before this point
454
  // the map contains only catch-all features and also controls their retention.
455
0
  if (table->get_feature_variations ().record_count ())
456
0
    for (auto p : *features_w_duplicates)
457
0
    {
458
0
      const OT::Feature& f = table->get_feature (p.first);
459
0
      const void *tag = &(table->get_feature_list ().get_tag (p.first));
460
0
      catch_all_record_idx_feature_map.set (p.first, hb_pair (&f, tag));
461
0
    }
462
0
#endif
463
0
}
Unexecuted instantiation: hb-subset-plan-layout.cc:void _closure_glyphs_lookups_features<OT::Layout::GSUB>(hb_subset_plan_t*, hb_set_t*, hb_map_t*, hb_map_t*, hb_map_t*, hb_hashmap_t<unsigned int, hb::unique_ptr<hb_set_t>, false>*, hb_hashmap_t<unsigned int, hb::shared_ptr<hb_set_t>, false>*, hb_hashmap_t<unsigned int, OT::Feature const*, false>*, hb_hashmap_t<unsigned int, hb::shared_ptr<hb_set_t>, false>*, hb_set_t&, hb_hashmap_t<unsigned int, hb_pair_t<void const*, void const*>, false>&)
Unexecuted instantiation: hb-subset-plan-layout.cc:void _closure_glyphs_lookups_features<OT::Layout::GPOS>(hb_subset_plan_t*, hb_set_t*, hb_map_t*, hb_map_t*, hb_map_t*, hb_hashmap_t<unsigned int, hb::unique_ptr<hb_set_t>, false>*, hb_hashmap_t<unsigned int, hb::shared_ptr<hb_set_t>, false>*, hb_hashmap_t<unsigned int, OT::Feature const*, false>*, hb_hashmap_t<unsigned int, hb::shared_ptr<hb_set_t>, false>*, hb_set_t&, hb_hashmap_t<unsigned int, hb_pair_t<void const*, void const*>, false>&)
464
465
void layout_nameid_closure (hb_subset_plan_t* plan,
466
                             hb_set_t* drop_tables)
467
0
{
468
0
  if (!drop_tables->has (HB_OT_TAG_GPOS))
469
0
  {
470
0
    hb_blob_ptr_t<GPOS> gpos = plan->source_table<GPOS> ();
471
0
    gpos->collect_name_ids (&plan->gpos_features, &plan->name_ids,
472
0
                            &plan->gpos_feature_substitutes_map,
473
0
                            plan->user_axes_location.is_empty () ? nullptr : &plan->gpos_feature_record_cond_idx_map,
474
0
                            &plan->gpos_old_features,
475
0
                            plan->all_axes_pinned);
476
0
  }
477
0
  if (!drop_tables->has (HB_OT_TAG_GSUB))
478
0
  {
479
0
    hb_blob_ptr_t<GSUB> gsub = plan->source_table<GSUB> ();
480
0
    gsub->collect_name_ids (&plan->gsub_features, &plan->name_ids,
481
0
                            &plan->gsub_feature_substitutes_map,
482
0
                            plan->user_axes_location.is_empty () ? nullptr : &plan->gsub_feature_record_cond_idx_map,
483
0
                            &plan->gsub_old_features,
484
0
                            plan->all_axes_pinned);
485
0
  }
486
0
}
487
488
void
489
layout_populate_gids_to_retain (hb_subset_plan_t* plan,
490
0
                    hb_set_t* drop_tables) {
491
0
  if (!drop_tables->has (HB_OT_TAG_GSUB))
492
    // closure all glyphs/lookups/features needed for GSUB substitutions.
493
0
    _closure_glyphs_lookups_features<GSUB> (
494
0
        plan,
495
0
        &plan->_glyphset_gsub,
496
0
        &plan->gsub_lookups,
497
0
        &plan->gsub_features,
498
0
        &plan->gsub_features_w_duplicates,
499
0
        &plan->gsub_langsys,
500
0
        &plan->gsub_feature_record_cond_idx_map,
501
0
        &plan->gsub_feature_substitutes_map,
502
0
        &plan->gsub_lookup_variations,
503
0
        plan->gsub_old_features,
504
0
        plan->gsub_old_feature_idx_tag_map);
505
506
0
  if (!drop_tables->has (HB_OT_TAG_GPOS))
507
0
    _closure_glyphs_lookups_features<GPOS> (
508
0
        plan,
509
0
        &plan->_glyphset_gsub,
510
0
        &plan->gpos_lookups,
511
0
        &plan->gpos_features,
512
0
        &plan->gpos_features_w_duplicates,
513
0
        &plan->gpos_langsys,
514
0
        &plan->gpos_feature_record_cond_idx_map,
515
0
        &plan->gpos_feature_substitutes_map,
516
0
        &plan->gpos_lookup_variations,
517
0
        plan->gpos_old_features,
518
0
        plan->gpos_old_feature_idx_tag_map);
519
0
}
520
521
#ifndef HB_NO_VAR
522
void
523
collect_layout_variation_indices (hb_subset_plan_t* plan)
524
0
{
525
0
  hb_blob_ptr_t<OT::GDEF> gdef = plan->source_table<OT::GDEF> ();
526
0
  hb_blob_ptr_t<GSUB> gsub = plan->source_table<GSUB> ();
527
0
  hb_blob_ptr_t<GPOS> gpos = plan->source_table<GPOS> ();
528
529
0
  if (!gdef->has_data () || !gdef->has_var_store ())
530
0
  {
531
0
    return;
532
0
  }
533
534
0
  hb_set_t varidx_set;
535
0
  OT::hb_collect_variation_indices_context_t c (&varidx_set,
536
0
                                                &plan->_glyphset_gsub,
537
0
                                                &plan->gpos_lookups);
538
0
  if (plan->normalized_coords && !plan->all_axes_pinned)
539
0
    c.condition_values = &plan->layout_condition_values;
540
0
  gdef->collect_variation_indices (&c);
541
542
0
  if (hb_ot_layout_has_substitution (plan->source))
543
0
    gsub->collect_feature_variation_indices (&c);
544
0
  if (hb_ot_layout_has_positioning (plan->source))
545
0
  {
546
0
    gpos->collect_feature_variation_indices (&c);
547
0
    gpos->collect_variation_indices (&c);
548
0
  }
549
550
0
#ifndef HB_NO_MATH
551
0
  if (!plan->drop_tables.has (HB_OT_TAG_MATH))
552
0
  {
553
0
    hb_blob_ptr_t<OT::MATH> math = plan->source_table<OT::MATH> ();
554
0
    auto *glyph_set = c.glyph_set;
555
0
    c.glyph_set = &plan->_glyphset_mathed;
556
0
    math->collect_variation_indices (&c, &plan->_glyphset_cmaped);
557
0
    c.glyph_set = glyph_set;
558
0
  }
559
0
#endif
560
561
0
  remap_variation_indices (gdef->get_var_store (),
562
0
                           varidx_set, plan->normalized_coords,
563
0
                           !plan->pinned_at_default,
564
0
                           plan->all_axes_pinned,
565
0
                           plan->layout_variation_idx_delta_map);
566
567
0
  generate_varstore_inner_maps (gdef->get_var_store (), varidx_set, plan->gdef_varstore_inner_maps);
568
0
  plan->check_success (!plan->layout_condition_values.in_error ());
569
0
}
570
#endif
571
572
#endif