Coverage Report

Created: 2025-11-09 06:40

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/harfbuzz/src/OT/glyf/glyf.hh
Line
Count
Source
1
#ifndef OT_GLYF_GLYF_HH
2
#define OT_GLYF_GLYF_HH
3
4
5
#include "../../hb-open-type.hh"
6
#include "../../hb-ot-head-table.hh"
7
#include "../../hb-ot-hmtx-table.hh"
8
#include "../../hb-ot-var-gvar-table.hh"
9
#include "../../hb-draw.hh"
10
#include "../../hb-paint.hh"
11
12
#include "glyf-helpers.hh"
13
#include "Glyph.hh"
14
#include "SubsetGlyph.hh"
15
#include "loca.hh"
16
#include "path-builder.hh"
17
18
19
namespace OT {
20
21
22
/*
23
 * glyf -- TrueType Glyph Data
24
 * https://docs.microsoft.com/en-us/typography/opentype/spec/glyf
25
 */
26
#define HB_OT_TAG_glyf HB_TAG('g','l','y','f')
27
28
struct glyf
29
{
30
  friend struct glyf_accelerator_t;
31
32
  static constexpr hb_tag_t tableTag = HB_OT_TAG_glyf;
33
34
  static bool has_valid_glyf_format(const hb_face_t* face)
35
0
  {
36
0
    const OT::head &head = *face->table.head;
37
0
    return head.indexToLocFormat <= 1 && head.glyphDataFormat <= 1;
38
0
  }
39
40
  bool sanitize (hb_sanitize_context_t *c HB_UNUSED) const
41
0
  {
42
0
    TRACE_SANITIZE (this);
43
    /* Runtime checks as eager sanitizing each glyph is costy */
44
0
    return_trace (true);
45
0
  }
46
47
  /* requires source of SubsetGlyph complains the identifier isn't declared */
48
  template <typename Iterator>
49
  bool serialize (hb_serialize_context_t *c,
50
      Iterator it,
51
                  bool use_short_loca,
52
      const hb_subset_plan_t *plan)
53
0
  {
54
0
    TRACE_SERIALIZE (this);
55
0
56
0
    unsigned init_len = c->length ();
57
0
    for (auto &_ : it)
58
0
      if (unlikely (!_.serialize (c, use_short_loca, plan)))
59
0
        return false;
60
0
61
0
    /* As a special case when all glyph in the font are empty, add a zero byte
62
0
     * to the table, so that OTS doesn’t reject it, and to make the table work
63
0
     * on Windows as well.
64
0
     * See https://github.com/khaledhosny/ots/issues/52 */
65
0
    if (init_len == c->length ())
66
0
    {
67
0
      HBUINT8 empty_byte;
68
0
      empty_byte = 0;
69
0
      c->copy (empty_byte);
70
0
    }
71
0
    return_trace (true);
72
0
  }
73
74
  /* Byte region(s) per glyph to output
75
     unpadded, hints removed if so requested
76
     If we fail to process a glyph we produce an empty (0-length) glyph */
77
  bool subset (hb_subset_context_t *c) const
78
0
  {
79
0
    TRACE_SUBSET (this);
80
0
81
0
    if (!has_valid_glyf_format (c->plan->source)) {
82
0
      // glyf format is unknown don't attempt to subset it.
83
0
      DEBUG_MSG (SUBSET, nullptr,
84
0
                 "unkown glyf format, dropping from subset.");
85
0
      return_trace (false);
86
0
    }
87
0
88
0
    hb_font_t *font = nullptr;
89
0
    if (c->plan->normalized_coords)
90
0
    {
91
0
      font = _create_font_for_instancing (c->plan);
92
0
      if (unlikely (!font))
93
0
  return_trace (false);
94
0
    }
95
0
96
0
    hb_vector_t<unsigned> padded_offsets;
97
0
    if (unlikely (!padded_offsets.alloc_exact (c->plan->new_to_old_gid_list.length)))
98
0
      return_trace (false);
99
0
100
0
    hb_vector_t<glyf_impl::SubsetGlyph> glyphs;
101
0
    if (!_populate_subset_glyphs (c->plan, font, glyphs))
102
0
    {
103
0
      hb_font_destroy (font);
104
0
      return_trace (false);
105
0
    }
106
0
107
0
    if (font)
108
0
      hb_font_destroy (font);
109
0
110
0
    unsigned max_offset = 0;
111
0
    for (auto &g : glyphs)
112
0
    {
113
0
      unsigned size = g.padded_size ();
114
0
      padded_offsets.push (size);
115
0
      max_offset += size;
116
0
    }
117
0
118
0
    bool use_short_loca = false;
119
0
    if (likely (!c->plan->force_long_loca))
120
0
      use_short_loca = max_offset < 0x1FFFF;
121
0
122
0
    if (!use_short_loca)
123
0
    {
124
0
      padded_offsets.resize (0);
125
0
      for (auto &g : glyphs)
126
0
  padded_offsets.push (g.length ());
127
0
    }
128
0
129
0
    auto *glyf_prime = c->serializer->start_embed <glyf> ();
130
0
    bool result = glyf_prime->serialize (c->serializer, hb_iter (glyphs), use_short_loca, c->plan);
131
0
    if (c->plan->normalized_coords && !c->plan->pinned_at_default)
132
0
      _free_compiled_subset_glyphs (glyphs);
133
0
134
0
    if (unlikely (!c->serializer->check_success (glyf_impl::_add_loca_and_head (c,
135
0
             padded_offsets.iter (),
136
0
             use_short_loca))))
137
0
      return_trace (false);
138
0
139
0
    return result;
140
0
  }
141
142
  bool
143
  _populate_subset_glyphs (const hb_subset_plan_t   *plan,
144
         hb_font_t                *font,
145
         hb_vector_t<glyf_impl::SubsetGlyph>& glyphs /* OUT */) const;
146
147
  hb_font_t *
148
  _create_font_for_instancing (const hb_subset_plan_t *plan) const;
149
150
  void _free_compiled_subset_glyphs (hb_vector_t<glyf_impl::SubsetGlyph> &glyphs) const
151
0
  {
152
0
    for (auto &g : glyphs)
153
0
      g.free_compiled_bytes ();
154
0
  }
155
156
  protected:
157
  UnsizedArrayOf<HBUINT8>
158
    dataZ;  /* Glyphs data. */
159
  public:
160
  DEFINE_SIZE_MIN (0);  /* In reality, this is UNBOUNDED() type; but since we always
161
       * check the size externally, allow Null() object of it by
162
       * defining it _MIN instead. */
163
};
164
165
struct glyf_accelerator_t
166
{
167
  glyf_accelerator_t (hb_face_t *face)
168
0
  {
169
0
    short_offset = false;
170
0
    num_glyphs = 0;
171
0
    loca_table = nullptr;
172
0
    glyf_table = nullptr;
173
0
#ifndef HB_NO_VAR
174
0
    gvar = nullptr;
175
#ifndef HB_NO_BEYOND_64K
176
    GVAR = nullptr;
177
#endif
178
0
#endif
179
0
    hmtx = nullptr;
180
0
#ifndef HB_NO_VERTICAL
181
0
    vmtx = nullptr;
182
0
#endif
183
0
    const OT::head &head = *face->table.head;
184
0
    if (!glyf::has_valid_glyf_format (face))
185
      /* Unknown format.  Leave num_glyphs=0, that takes care of disabling us. */
186
0
      return;
187
0
    short_offset = 0 == head.indexToLocFormat;
188
189
0
    loca_table = face->table.loca.get_blob (); // Needs no destruct!
190
0
    glyf_table = hb_sanitize_context_t ().reference_table<glyf> (face);
191
0
#ifndef HB_NO_VAR
192
0
    gvar = face->table.gvar;
193
#ifndef HB_NO_BEYOND_64K
194
    GVAR = face->table.GVAR;
195
#endif
196
0
#endif
197
0
    hmtx = face->table.hmtx;
198
0
#ifndef HB_NO_VERTICAL
199
0
    vmtx = face->table.vmtx;
200
0
#endif
201
202
0
    num_glyphs = hb_max (1u, loca_table.get_length () / (short_offset ? 2 : 4)) - 1;
203
0
    num_glyphs = hb_min (num_glyphs, face->get_num_glyphs ());
204
0
  }
205
  ~glyf_accelerator_t ()
206
0
  {
207
0
    auto *scratch = cached_scratch.get_relaxed ();
208
0
    if (scratch)
209
0
    {
210
0
      scratch->~hb_glyf_scratch_t ();
211
0
      hb_free (scratch);
212
0
    }
213
214
0
    glyf_table.destroy ();
215
0
  }
216
217
0
  bool has_data () const { return num_glyphs; }
218
219
  protected:
220
  template<typename T>
221
  bool get_points (hb_font_t *font, hb_codepoint_t gid, T consumer,
222
       hb_array_t<const int> coords,
223
       hb_glyf_scratch_t &scratch,
224
       hb_scalar_cache_t *gvar_cache = nullptr) const
225
0
  {
226
0
    if (gid >= num_glyphs) return false;
227
228
0
    auto &all_points = scratch.all_points;
229
0
    all_points.resize (0);
230
231
0
    bool phantom_only = !consumer.is_consuming_contour_points ();
232
0
    if (unlikely (!glyph_for_gid (gid).get_points (font, *this, all_points, scratch, nullptr, nullptr, nullptr, true, true, phantom_only, coords, gvar_cache)))
233
0
      return false;
234
235
0
    unsigned count = all_points.length;
236
0
    assert (count >= glyf_impl::PHANTOM_COUNT);
237
0
    count -= glyf_impl::PHANTOM_COUNT;
238
239
0
    if (consumer.is_consuming_contour_points ())
240
0
    {
241
0
      auto *points = all_points.arrayZ;
242
243
0
      if (false)
244
0
      {
245
  /* Our path-builder was designed to work with this simple loop.
246
   * But FreeType and CoreText do it differently, so we match those
247
   * with the other, more complicated, code branch below. */
248
0
  for (unsigned i = 0; i < count; i++)
249
0
  {
250
0
    consumer.consume_point (points[i]);
251
0
    if (points[i].is_end_point)
252
0
      consumer.contour_end ();
253
0
  }
254
0
      }
255
0
      else
256
0
      {
257
0
  for (unsigned i = 0; i < count; i++)
258
0
  {
259
    // Start of a contour.
260
0
    if (points[i].flag & glyf_impl::SimpleGlyph::FLAG_ON_CURVE)
261
0
    {
262
      // First point is on-curve. Draw the contour.
263
0
      for (; i < count; i++)
264
0
      {
265
0
        consumer.consume_point (points[i]);
266
0
        if (points[i].is_end_point)
267
0
        {
268
0
    consumer.contour_end ();
269
0
    break;
270
0
        }
271
0
      }
272
0
    }
273
0
    else
274
0
    {
275
0
      unsigned start = i;
276
277
      // Find end of the contour.
278
0
      for (; i < count; i++)
279
0
        if (points[i].is_end_point)
280
0
    break;
281
282
0
      unsigned end = i;
283
284
      // Enough to start from the end. Our path-builder takes care of the rest.
285
0
      if (likely (end < count)) // Can only fail in case of alloc failure *maybe*.
286
0
        consumer.consume_point (points[end]);
287
288
0
      for (i = start; i < end; i++)
289
0
        consumer.consume_point (points[i]);
290
291
0
      consumer.contour_end ();
292
0
    }
293
0
  }
294
0
      }
295
296
0
      consumer.points_end ();
297
0
    }
298
299
    /* Where to write phantoms, nullptr if not requested */
300
0
    contour_point_t *phantoms = consumer.get_phantoms_sink ();
301
0
    if (phantoms)
302
0
      for (unsigned i = 0; i < glyf_impl::PHANTOM_COUNT; ++i)
303
0
  phantoms[i] = all_points.arrayZ[count + i];
304
305
0
    return true;
306
0
  }
Unexecuted instantiation: bool OT::glyf_accelerator_t::get_points<OT::glyf_accelerator_t::points_aggregator_t>(hb_font_t*, unsigned int, OT::glyf_accelerator_t::points_aggregator_t, hb_array_t<int const>, hb_glyf_scratch_t&, OT::hb_scalar_cache_t*) const
Unexecuted instantiation: bool OT::glyf_accelerator_t::get_points<OT::glyf_impl::path_builder_t>(hb_font_t*, unsigned int, OT::glyf_impl::path_builder_t, hb_array_t<int const>, hb_glyf_scratch_t&, OT::hb_scalar_cache_t*) const
307
308
  public:
309
310
#ifndef HB_NO_VAR
311
  struct points_aggregator_t
312
  {
313
    hb_font_t *font;
314
    hb_glyph_extents_t *extents;
315
    contour_point_t *phantoms;
316
    bool scaled;
317
318
    struct contour_bounds_t
319
    {
320
0
      contour_bounds_t () { min_x = min_y = FLT_MAX; max_x = max_y = -FLT_MAX; }
321
322
      void add (const contour_point_t &p)
323
0
      {
324
0
  min_x = hb_min (min_x, p.x);
325
0
  min_y = hb_min (min_y, p.y);
326
0
  max_x = hb_max (max_x, p.x);
327
0
  max_y = hb_max (max_y, p.y);
328
0
      }
329
330
0
      bool empty () const { return (min_x >= max_x) || (min_y >= max_y); }
331
332
      void get_extents (hb_font_t *font, hb_glyph_extents_t *extents, bool scaled)
333
0
      {
334
0
  if (unlikely (empty ()))
335
0
  {
336
0
    extents->width = 0;
337
0
    extents->x_bearing = 0;
338
0
    extents->height = 0;
339
0
    extents->y_bearing = 0;
340
0
    return;
341
0
  }
342
0
  {
343
0
    extents->x_bearing = roundf (min_x);
344
0
    extents->width = roundf (max_x - extents->x_bearing);
345
0
    extents->y_bearing = roundf (max_y);
346
0
    extents->height = roundf (min_y - extents->y_bearing);
347
348
0
    if (scaled)
349
0
      font->scale_glyph_extents (extents);
350
0
  }
351
0
      }
352
353
      protected:
354
      float min_x, min_y, max_x, max_y;
355
    } bounds;
356
357
    points_aggregator_t (hb_font_t *font_, hb_glyph_extents_t *extents_, contour_point_t *phantoms_, bool scaled_)
358
0
    {
359
0
      font = font_;
360
0
      extents = extents_;
361
0
      phantoms = phantoms_;
362
0
      scaled = scaled_;
363
0
      if (extents) bounds = contour_bounds_t ();
364
0
    }
365
366
    HB_ALWAYS_INLINE
367
0
    void consume_point (const contour_point_t &point) { bounds.add (point); }
368
0
    void contour_end () {}
369
0
    void points_end () { bounds.get_extents (font, extents, scaled); }
370
371
0
    bool is_consuming_contour_points () { return extents; }
372
0
    contour_point_t *get_phantoms_sink () { return phantoms; }
373
  };
374
375
#ifndef HB_NO_VAR
376
  unsigned
377
  get_advance_with_var_unscaled (hb_codepoint_t gid,
378
         hb_font_t *font,
379
         bool is_vertical,
380
          hb_glyf_scratch_t &scratch,
381
         hb_scalar_cache_t *gvar_cache = nullptr) const
382
0
  {
383
0
    if (unlikely (gid >= num_glyphs)) return 0;
384
385
0
    bool success = false;
386
387
0
    contour_point_t phantoms[glyf_impl::PHANTOM_COUNT];
388
0
    success = get_points (font, gid, points_aggregator_t (font, nullptr, phantoms, false),
389
0
        hb_array (font->coords,
390
0
            font->has_nonzero_coords ? font->num_coords : 0),
391
0
        scratch, gvar_cache);
392
0
    if (unlikely (!success))
393
0
    {
394
0
      unsigned upem = font->face->get_upem ();
395
0
      return is_vertical ? upem : upem / 2;
396
0
    }
397
398
0
    float result = is_vertical
399
0
     ? phantoms[glyf_impl::PHANTOM_TOP].y - phantoms[glyf_impl::PHANTOM_BOTTOM].y
400
0
     : phantoms[glyf_impl::PHANTOM_RIGHT].x - phantoms[glyf_impl::PHANTOM_LEFT].x;
401
0
    return hb_clamp (roundf (result), 0.f, (float) UINT_MAX / 2);
402
0
  }
403
404
  float
405
  get_v_origin_with_var_unscaled (hb_codepoint_t gid,
406
          hb_font_t *font,
407
          hb_glyf_scratch_t &scratch,
408
          hb_scalar_cache_t *gvar_cache = nullptr) const
409
0
  {
410
0
    if (unlikely (gid >= num_glyphs)) return 0;
411
412
0
    bool success = false;
413
414
0
    contour_point_t phantoms[glyf_impl::PHANTOM_COUNT];
415
0
    success = get_points (font, gid, points_aggregator_t (font, nullptr, phantoms, false),
416
0
        hb_array (font->coords,
417
0
            font->has_nonzero_coords ? font->num_coords : 0),
418
0
        scratch, gvar_cache);
419
0
    if (unlikely (!success))
420
0
    {
421
0
      return font->face->get_upem ();
422
0
    }
423
424
0
    return phantoms[glyf_impl::PHANTOM_TOP].y;
425
0
  }
426
#endif
427
#endif
428
429
  public:
430
431
  bool get_extents (hb_font_t *font,
432
        hb_codepoint_t gid,
433
        hb_glyph_extents_t *extents) const
434
0
  { return get_extents_at (font, gid, extents, hb_array (font->coords,
435
0
               font->has_nonzero_coords ? font->num_coords : 0)); }
436
437
  bool get_extents_at (hb_font_t *font,
438
           hb_codepoint_t gid,
439
           hb_glyph_extents_t *extents,
440
           hb_array_t<const int> coords) const
441
0
  {
442
0
    if (unlikely (gid >= num_glyphs)) return false;
443
444
0
#ifndef HB_NO_VAR
445
0
    if (coords)
446
0
    {
447
0
      hb_glyf_scratch_t *scratch = acquire_scratch ();
448
0
      if (unlikely (!scratch)) return false;
449
0
      bool ret = get_points (font,
450
0
           gid,
451
0
           points_aggregator_t (font, extents, nullptr, true),
452
0
           coords,
453
0
           *scratch);
454
0
      release_scratch (scratch);
455
0
      return ret;
456
0
    }
457
0
#endif
458
0
    return glyph_for_gid (gid).get_extents_without_var_scaled (font, *this, extents);
459
0
  }
460
461
  const glyf_impl::Glyph
462
  glyph_for_gid (hb_codepoint_t gid, bool needs_padding_removal = false) const
463
0
  {
464
0
    if (unlikely (gid >= num_glyphs)) return glyf_impl::Glyph ();
465
466
0
    unsigned int start_offset, end_offset;
467
468
0
    if (short_offset)
469
0
    {
470
0
      const HBUINT16 *offsets = (const HBUINT16 *) loca_table->dataZ.arrayZ;
471
0
      start_offset = 2 * offsets[gid];
472
0
      end_offset   = 2 * offsets[gid + 1];
473
0
    }
474
0
    else
475
0
    {
476
0
      const HBUINT32 *offsets = (const HBUINT32 *) loca_table->dataZ.arrayZ;
477
0
      start_offset = offsets[gid];
478
0
      end_offset   = offsets[gid + 1];
479
0
    }
480
481
0
    if (unlikely (start_offset > end_offset || end_offset > glyf_table.get_length ()))
482
0
      return glyf_impl::Glyph ();
483
484
0
    glyf_impl::Glyph glyph (hb_bytes_t ((const char *) this->glyf_table + start_offset,
485
0
           end_offset - start_offset), gid);
486
0
    return needs_padding_removal ? glyf_impl::Glyph (glyph.trim_padding (), gid) : glyph;
487
0
  }
488
489
  bool
490
  get_path (hb_font_t *font, hb_codepoint_t gid, hb_draw_session_t &draw_session, hb_scalar_cache_t *gvar_cache = nullptr) const
491
0
  {
492
0
    if (!has_data ()) return false;
493
494
0
    hb_glyf_scratch_t *scratch = acquire_scratch ();
495
0
    if (unlikely (!scratch)) return true;
496
497
0
    bool ret = get_points (font, gid, glyf_impl::path_builder_t (font, draw_session),
498
0
         hb_array (font->coords,
499
0
             font->has_nonzero_coords ? font->num_coords : 0),
500
0
         *scratch,
501
0
          gvar_cache);
502
503
0
    release_scratch (scratch);
504
505
0
    return ret;
506
0
  }
507
508
  bool
509
  get_path_at (hb_font_t *font, hb_codepoint_t gid, hb_draw_session_t &draw_session,
510
         hb_array_t<const int> coords,
511
         hb_glyf_scratch_t &scratch,
512
         hb_scalar_cache_t *gvar_cache = nullptr) const
513
0
  {
514
0
    if (!has_data ()) return false;
515
0
    return get_points (font, gid, glyf_impl::path_builder_t (font, draw_session),
516
0
           coords,
517
0
           scratch,
518
0
           gvar_cache);
519
0
  }
520
521
522
  hb_glyf_scratch_t *acquire_scratch () const
523
0
  {
524
0
    if (!has_data ()) return nullptr;
525
0
    hb_glyf_scratch_t *scratch = cached_scratch.get_acquire ();
526
0
    if (!scratch || unlikely (!cached_scratch.cmpexch (scratch, nullptr)))
527
0
    {
528
0
      scratch = (hb_glyf_scratch_t *) hb_calloc (1, sizeof (hb_glyf_scratch_t));
529
0
      if (unlikely (!scratch))
530
0
  return nullptr;
531
0
    }
532
0
    return scratch;
533
0
  }
534
  void release_scratch (hb_glyf_scratch_t *scratch) const
535
0
  {
536
0
    if (!scratch)
537
0
      return;
538
0
    if (!cached_scratch.cmpexch (nullptr, scratch))
539
0
    {
540
0
      scratch->~hb_glyf_scratch_t ();
541
0
      hb_free (scratch);
542
0
    }
543
0
  }
544
545
#ifndef HB_NO_VAR
546
  const gvar_accelerator_t *gvar;
547
#ifndef HB_NO_BEYOND_64K
548
  const GVAR_accelerator_t *GVAR;
549
#endif
550
#endif
551
  const hmtx_accelerator_t *hmtx;
552
#ifndef HB_NO_VERTICAL
553
  const vmtx_accelerator_t *vmtx;
554
#endif
555
556
  private:
557
  bool short_offset;
558
  unsigned int num_glyphs;
559
  hb_blob_ptr_t<loca> loca_table;
560
  hb_blob_ptr_t<glyf> glyf_table;
561
  mutable hb_atomic_t<hb_glyf_scratch_t *> cached_scratch;
562
};
563
564
565
inline bool
566
glyf::_populate_subset_glyphs (const hb_subset_plan_t   *plan,
567
             hb_font_t *font,
568
             hb_vector_t<glyf_impl::SubsetGlyph>& glyphs /* OUT */) const
569
0
{
570
0
  OT::glyf_accelerator_t glyf (plan->source);
571
0
  if (!glyphs.alloc_exact (plan->new_to_old_gid_list.length)) return false;
572
0
573
0
  for (const auto &pair : plan->new_to_old_gid_list)
574
0
  {
575
0
    hb_codepoint_t new_gid = pair.first;
576
0
    hb_codepoint_t old_gid = pair.second;
577
0
    glyf_impl::SubsetGlyph *p = glyphs.push ();
578
0
    glyf_impl::SubsetGlyph& subset_glyph = *p;
579
0
    subset_glyph.old_gid = old_gid;
580
0
581
0
    if (unlikely (old_gid == 0 && new_gid == 0 &&
582
0
                  !(plan->flags & HB_SUBSET_FLAGS_NOTDEF_OUTLINE)) &&
583
0
                  !plan->normalized_coords)
584
0
      subset_glyph.source_glyph = glyf_impl::Glyph ();
585
0
    else
586
0
    {
587
0
      /* If plan has an accelerator, the preprocessing step already trimmed glyphs.
588
0
       * Don't trim them again! */
589
0
      subset_glyph.source_glyph = glyf.glyph_for_gid (subset_glyph.old_gid, !plan->accelerator);
590
0
    }
591
0
592
0
    if (plan->flags & HB_SUBSET_FLAGS_NO_HINTING)
593
0
      subset_glyph.drop_hints_bytes ();
594
0
    else
595
0
      subset_glyph.dest_start = subset_glyph.source_glyph.get_bytes ();
596
0
597
0
    if (font)
598
0
    {
599
0
      if (unlikely (!subset_glyph.compile_bytes_with_deltas (plan, font, glyf)))
600
0
      {
601
0
        // when pinned at default, only bounds are updated, thus no need to free
602
0
        if (!plan->pinned_at_default)
603
0
          _free_compiled_subset_glyphs (glyphs);
604
0
        return false;
605
0
      }
606
0
    }
607
0
  }
608
0
  return true;
609
0
}
610
611
inline hb_font_t *
612
glyf::_create_font_for_instancing (const hb_subset_plan_t *plan) const
613
0
{
614
0
  hb_font_t *font = hb_font_create (plan->source);
615
0
  if (unlikely (font == hb_font_get_empty ())) return nullptr;
616
0
617
0
  hb_vector_t<hb_variation_t> vars;
618
0
  if (unlikely (!vars.alloc (plan->user_axes_location.get_population (), true)))
619
0
  {
620
0
    hb_font_destroy (font);
621
0
    return nullptr;
622
0
  }
623
0
624
0
  for (auto _ : plan->user_axes_location)
625
0
  {
626
0
    hb_variation_t var;
627
0
    var.tag = _.first;
628
0
    var.value = _.second.middle;
629
0
    vars.push (var);
630
0
  }
631
0
632
0
#ifndef HB_NO_VAR
633
0
  hb_font_set_variations (font, vars.arrayZ, plan->user_axes_location.get_population ());
634
0
#endif
635
0
  return font;
636
0
}
637
638
639
} /* namespace OT */
640
641
642
#endif /* OT_GLYF_GLYF_HH */