Coverage Report

Created: 2026-10-30 06:49

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/harfbuzz/src/OT/glyf/Glyph.hh
Line
Count
Source
1
#ifndef OT_GLYF_GLYPH_HH
2
#define OT_GLYF_GLYPH_HH
3
4
5
#include "../../hb-open-type.hh"
6
7
#include "GlyphHeader.hh"
8
#include "SimpleGlyph.hh"
9
#include "CompositeGlyph.hh"
10
11
12
namespace OT {
13
14
struct glyf_accelerator_t;
15
16
namespace glyf_impl {
17
18
19
enum phantom_point_index_t
20
{
21
  PHANTOM_LEFT   = 0,
22
  PHANTOM_RIGHT  = 1,
23
  PHANTOM_TOP    = 2,
24
  PHANTOM_BOTTOM = 3,
25
  PHANTOM_COUNT  = 4
26
};
27
28
struct Glyph
29
{
30
  enum glyph_type_t {
31
    EMPTY,
32
    SIMPLE,
33
    COMPOSITE,
34
  };
35
36
  public:
37
  composite_iter_t get_composite_iterator () const
38
0
  {
39
0
    if (type != COMPOSITE) return composite_iter_t ();
40
0
    return CompositeGlyph (*header, bytes, extended).iter ();
41
0
  }
42
43
  const hb_bytes_t trim_padding () const
44
0
  {
45
0
    switch (type) {
46
0
    case COMPOSITE: return CompositeGlyph (*header, bytes, extended).trim_padding ();
47
0
    case SIMPLE:    return SimpleGlyph (*header, bytes).trim_padding ();
48
0
    case EMPTY:     return bytes;
49
0
    default:        return bytes;
50
0
    }
51
0
  }
52
53
  void drop_hints ()
54
0
  {
55
0
    switch (type) {
56
0
    case COMPOSITE: CompositeGlyph (*header, bytes, extended).drop_hints (); return;
57
0
    case SIMPLE:    SimpleGlyph (*header, bytes).drop_hints (); return;
58
0
    case EMPTY:
59
0
      if (bytes.length >= GlyphHeader::static_size)
60
0
        SimpleGlyph (*header, bytes).drop_hints ();
61
0
      return;
62
0
    }
63
0
  }
64
65
  void set_overlaps_flag ()
66
0
  {
67
0
    switch (type) {
68
0
    case COMPOSITE: CompositeGlyph (*header, bytes, extended).set_overlaps_flag (); return;
69
0
    case SIMPLE:    SimpleGlyph (*header, bytes).set_overlaps_flag (); return;
70
0
    case EMPTY:     return;
71
0
    }
72
0
  }
73
74
  void drop_hints_bytes (hb_bytes_t &dest_start, hb_bytes_t &dest_end) const
75
0
  {
76
0
    switch (type) {
77
0
    case COMPOSITE: CompositeGlyph (*header, bytes, extended).drop_hints_bytes (dest_start); return;
78
0
    case SIMPLE:    SimpleGlyph (*header, bytes).drop_hints_bytes (dest_start, dest_end); return;
79
0
    case EMPTY:
80
0
      dest_start = bytes;
81
0
      dest_end = hb_bytes_t ();
82
0
      if (bytes.length >= GlyphHeader::static_size &&
83
0
          SimpleGlyph (*header, bytes).has_instructions_length ())
84
0
        SimpleGlyph (*header, bytes).drop_hints_bytes (dest_start, dest_end);
85
0
      return;
86
0
    }
87
0
  }
88
89
  bool is_composite () const
90
0
  { return type == COMPOSITE; }
91
92
  bool get_all_points_without_var (const hb_face_t *face,
93
                                   contour_point_vector_t &points /* OUT */) const
94
0
  {
95
0
    switch (type) {
96
0
    case SIMPLE:
97
0
      if (unlikely (!SimpleGlyph (*header, bytes).get_contour_points (points, extended)))
98
0
        return false;
99
0
      break;
100
0
    case COMPOSITE:
101
0
    {
102
0
      for (auto &item : get_composite_iterator ())
103
0
        if (unlikely (!item.get_points (points, extended))) return false;
104
0
      break;
105
0
    }
106
0
    case EMPTY:
107
0
      break;
108
0
    }
109
0
110
0
    /* Init phantom points */
111
0
    if (unlikely (!points.resize (points.length + PHANTOM_COUNT))) return false;
112
0
    hb_array_t<contour_point_t> phantoms = points.as_array ().sub_array (points.length - PHANTOM_COUNT, PHANTOM_COUNT);
113
0
    {
114
0
      // Duplicated code.
115
0
      int lsb = 0;
116
0
      face->table.hmtx->get_leading_bearing_without_var_unscaled (gid, &lsb);
117
0
      int h_delta = (int) header->xMin - lsb;
118
0
      HB_UNUSED int tsb = 0;
119
0
#ifndef HB_NO_VERTICAL
120
0
      face->table.vmtx->get_leading_bearing_without_var_unscaled (gid, &tsb);
121
0
#endif
122
0
      int v_orig  = (int) header->yMax + tsb;
123
0
      unsigned h_adv = face->table.hmtx->get_advance_without_var_unscaled (gid);
124
0
      unsigned v_adv =
125
0
#ifndef HB_NO_VERTICAL
126
0
                       face->table.vmtx->get_advance_without_var_unscaled (gid)
127
0
#else
128
0
                       - face->get_upem ()
129
0
#endif
130
0
                       ;
131
0
      phantoms[PHANTOM_LEFT].x = h_delta;
132
0
      phantoms[PHANTOM_RIGHT].x = (int) h_adv + h_delta;
133
0
      phantoms[PHANTOM_TOP].y = v_orig;
134
0
      phantoms[PHANTOM_BOTTOM].y = v_orig - (int) v_adv;
135
0
    }
136
0
    return true;
137
0
  }
138
139
  void update_mtx (const hb_subset_plan_t *plan,
140
                   int xMin, int xMax,
141
                   int yMin, int yMax,
142
                   const contour_point_vector_t &all_points) const
143
0
  {
144
0
    hb_codepoint_t new_gid = 0;
145
0
    if (!plan->new_gid_for_old_gid (gid, &new_gid))
146
0
      return;
147
0
148
0
    if (type != EMPTY)
149
0
    {
150
0
      plan->bounds_width_vec[new_gid] = xMax - xMin;
151
0
      plan->bounds_height_vec[new_gid] = yMax - yMin;
152
0
    }
153
0
154
0
    unsigned len = all_points.length;
155
0
    float leftSideX = all_points[len - 4].x;
156
0
    float rightSideX = all_points[len - 3].x;
157
0
    float topSideY = all_points[len - 2].y;
158
0
    float bottomSideY = all_points[len - 1].y;
159
0
160
0
    uint32_t hash = hb_hash (new_gid);
161
0
162
0
    signed hori_aw = roundf (rightSideX - leftSideX);
163
0
    if (hori_aw < 0) hori_aw = 0;
164
0
    int lsb = roundf (xMin - leftSideX);
165
0
    plan->hmtx_map.set_with_hash (new_gid, hash, hb_pair ((unsigned) hori_aw, lsb));
166
0
    //flag value should be computed using non-empty glyphs
167
0
    if (type != EMPTY && lsb != xMin)
168
0
      plan->head_maxp_info.allXMinIsLsb = false;
169
0
170
0
    signed vert_aw = roundf (topSideY - bottomSideY);
171
0
    if (vert_aw < 0) vert_aw = 0;
172
0
    int tsb = roundf (topSideY - yMax);
173
0
    plan->vmtx_map.set_with_hash (new_gid, hash, hb_pair ((unsigned) vert_aw, tsb));
174
0
  }
175
176
  bool compile_header_bytes (const hb_subset_plan_t *plan,
177
                             const contour_point_vector_t &all_points,
178
                             hb_bytes_t &dest_bytes /* OUT */) const
179
0
  {
180
0
    GlyphHeader *glyph_header = nullptr;
181
0
    if (type != EMPTY && all_points.length >= 4)
182
0
    {
183
0
      glyph_header = (GlyphHeader *) hb_calloc (1, GlyphHeader::static_size);
184
0
      if (unlikely (!glyph_header)) return false;
185
0
    }
186
0
187
0
    float xMin = 0, xMax = 0;
188
0
    float yMin = 0, yMax = 0;
189
0
    if (all_points.length > 4)
190
0
    {
191
0
      xMin = xMax = all_points[0].x;
192
0
      yMin = yMax = all_points[0].y;
193
0
194
0
      unsigned count = all_points.length - 4;
195
0
      for (unsigned i = 1; i < count; i++)
196
0
      {
197
0
  float x = all_points[i].x;
198
0
  float y = all_points[i].y;
199
0
  xMin = hb_min (xMin, x);
200
0
  xMax = hb_max (xMax, x);
201
0
  yMin = hb_min (yMin, y);
202
0
  yMax = hb_max (yMax, y);
203
0
      }
204
0
    }
205
0
206
0
207
0
    // These are destined for storage in a 16 bit field to clamp the values to
208
0
    // fit into a 16 bit signed integer.
209
0
    int rounded_xMin = hb_clamp (roundf (xMin), -32768.0f, 32767.0f);
210
0
    int rounded_xMax = hb_clamp (roundf (xMax), -32768.0f, 32767.0f);
211
0
    int rounded_yMin = hb_clamp (roundf (yMin), -32768.0f, 32767.0f);
212
0
    int rounded_yMax = hb_clamp (roundf (yMax), -32768.0f, 32767.0f);
213
0
214
0
    update_mtx (plan, rounded_xMin, rounded_xMax, rounded_yMin, rounded_yMax, all_points);
215
0
216
0
    if (type != EMPTY)
217
0
    {
218
0
      plan->head_maxp_info.xMin = hb_min (plan->head_maxp_info.xMin, rounded_xMin);
219
0
      plan->head_maxp_info.yMin = hb_min (plan->head_maxp_info.yMin, rounded_yMin);
220
0
      plan->head_maxp_info.xMax = hb_max (plan->head_maxp_info.xMax, rounded_xMax);
221
0
      plan->head_maxp_info.yMax = hb_max (plan->head_maxp_info.yMax, rounded_yMax);
222
0
    }
223
0
224
0
    /* Empty glyphs only include phantom points; update metrics and return. */
225
0
    if (!glyph_header)
226
0
      return true;
227
0
228
0
    glyph_header->numberOfContours = header->numberOfContours;
229
0
230
0
    glyph_header->xMin = rounded_xMin;
231
0
    glyph_header->yMin = rounded_yMin;
232
0
    glyph_header->xMax = rounded_xMax;
233
0
    glyph_header->yMax = rounded_yMax;
234
0
235
0
    dest_bytes = hb_bytes_t ((const char *)glyph_header, GlyphHeader::static_size);
236
0
    return true;
237
0
  }
238
239
  bool compile_bytes_with_deltas (const hb_subset_plan_t *plan,
240
                                  hb_font_t *font,
241
                                  const glyf_accelerator_t &glyf,
242
                                  hb_bytes_t &dest_start,  /* IN/OUT */
243
                                  hb_bytes_t &dest_end /* OUT */)
244
0
  {
245
0
    contour_point_vector_t all_points, points_with_deltas;
246
0
    unsigned composite_contours = 0;
247
0
    head_maxp_info_t *head_maxp_info_p = &plan->head_maxp_info;
248
0
    unsigned *composite_contours_p = &composite_contours;
249
0
250
0
    // don't compute head/maxp values when glyph has no contours(type is EMPTY)
251
0
    // also ignore .notdef glyph when --notdef-outline is not enabled
252
0
    if (type == EMPTY ||
253
0
        (gid == 0 && !(plan->flags & HB_SUBSET_FLAGS_NOTDEF_OUTLINE)))
254
0
    {
255
0
      head_maxp_info_p = nullptr;
256
0
      composite_contours_p = nullptr;
257
0
    }
258
0
259
0
    hb_glyf_scratch_t scratch;
260
0
    if (!get_points (font, glyf, all_points, scratch, &points_with_deltas, head_maxp_info_p, composite_contours_p, false, false))
261
0
      return false;
262
0
263
0
    // .notdef, set type to empty so we only update metrics and don't compile bytes for
264
0
    // it
265
0
    if (gid == 0 &&
266
0
        !(plan->flags & HB_SUBSET_FLAGS_NOTDEF_OUTLINE))
267
0
    {
268
0
      type = EMPTY;
269
0
      dest_start = hb_bytes_t ();
270
0
      dest_end = hb_bytes_t ();
271
0
    }
272
0
273
0
    // Recompile even at default: a previous partial instance may have
274
0
    // rounded component offsets and child points independently.
275
0
    switch (type)
276
0
    {
277
0
    case COMPOSITE:
278
0
      if (!CompositeGlyph (*header, bytes, extended).compile_bytes_with_deltas (dest_start,
279
0
                      points_with_deltas,
280
0
                      dest_end))
281
0
        return false;
282
0
      break;
283
0
    case SIMPLE:
284
0
      if (!SimpleGlyph (*header, bytes).compile_bytes_with_deltas (all_points,
285
0
                     plan->flags & HB_SUBSET_FLAGS_NO_HINTING,
286
0
                     dest_end))
287
0
        return false;
288
0
      break;
289
0
    case EMPTY:
290
0
      dest_start = hb_bytes_t ();
291
0
      dest_end = hb_bytes_t ();
292
0
      /* Zero-contour glyphs may carry instructions for phantom points.
293
0
       * Copy them into owned memory like the other compiled glyphs. */
294
0
      if (!(plan->flags & HB_SUBSET_FLAGS_NO_HINTING) &&
295
0
          bytes.length >= GlyphHeader::static_size &&
296
0
          header->numberOfContours == 0)
297
0
      {
298
0
        SimpleGlyph simple (*header, bytes);
299
0
        unsigned length = simple.instructions_length ();
300
0
        if (length)
301
0
        {
302
0
          length = simple.length (length);
303
0
          char *p = (char *) hb_malloc (length);
304
0
          if (unlikely (!p)) return false;
305
0
          hb_memcpy (p, bytes.arrayZ, length);
306
0
          dest_start = hb_bytes_t (p, length);
307
0
        }
308
0
      }
309
0
      break;
310
0
    }
311
0
312
0
    if (!compile_header_bytes (plan, all_points, dest_start))
313
0
    {
314
0
      dest_end.fini ();
315
0
      return false;
316
0
    }
317
0
    return true;
318
0
  }
319
320
321
  /* Note: Recursively calls itself.
322
   * all_points includes phantom points
323
   */
324
  template <typename accelerator_t>
325
  bool get_points (hb_font_t *font, const accelerator_t &glyf_accelerator,
326
       contour_point_vector_t &all_points /* OUT */,
327
       hb_glyf_scratch_t &scratch,
328
       contour_point_vector_t *points_with_deltas = nullptr, /* OUT */
329
       head_maxp_info_t * head_maxp_info = nullptr, /* OUT */
330
       unsigned *composite_contours = nullptr, /* OUT */
331
       bool shift_points_hori = true,
332
       bool use_my_metrics = true,
333
       bool phantom_only = false,
334
       hb_array_t<const int> coords = hb_array_t<const int> (),
335
       hb_scalar_cache_t *gvar_cache = nullptr,
336
       unsigned int depth = 0,
337
       unsigned *edge_count = nullptr,
338
       int64_t *budget = nullptr) const
339
0
  {
340
0
    if (unlikely (depth > HB_MAX_NESTING_LEVEL)) return false;
341
0
    unsigned stack_edge_count = 0;
342
0
    if (!edge_count) edge_count = &stack_edge_count;
343
0
    if (unlikely (*edge_count > HB_MAX_GRAPH_EDGE_COUNT)) return false;
344
0
    (*edge_count)++;
345
346
0
    int64_t stack_budget = HB_BUDGET_GLYPH;
347
0
    if (!budget) budget = &stack_budget;
348
0
    if (unlikely (!hb_budget_spend (*budget, HB_BUDGET_16))) return false;
349
350
0
    if (head_maxp_info)
351
0
    {
352
0
      head_maxp_info->maxComponentDepth = hb_max (head_maxp_info->maxComponentDepth, depth);
353
0
    }
354
355
0
    if (!coords && font->has_nonzero_coords)
356
0
      coords = hb_array (font->coords, font->num_coords);
357
358
0
    contour_point_vector_t &points = type == SIMPLE ? all_points : scratch.comp_points;
359
0
    unsigned old_length = points.length;
360
361
0
    switch (type) {
362
0
    case SIMPLE:
363
0
      if (depth == 0 && head_maxp_info)
364
0
        head_maxp_info->maxContours = hb_max (head_maxp_info->maxContours, (unsigned) header->numberOfContours);
365
0
      if (depth > 0 && composite_contours)
366
0
        *composite_contours += (unsigned) header->numberOfContours;
367
0
      if (unlikely (!SimpleGlyph (*header, bytes).get_contour_points (all_points, extended, phantom_only, budget)))
368
0
  return false;
369
0
      break;
370
0
    case COMPOSITE:
371
0
    {
372
0
      for (auto &item : get_composite_iterator ())
373
0
      {
374
        /* Preparing a component is a traversal step, even if cycle
375
         * detection later skips its child glyph. */
376
0
        if (unlikely (!hb_budget_spend (*budget, HB_BUDGET_16) ||
377
0
                      !item.get_points (points, extended)))
378
0
        {
379
0
          points.resize (old_length);
380
0
          return false;
381
0
        }
382
0
      }
383
0
      break;
384
0
    }
385
0
    case EMPTY:
386
0
      break;
387
0
    }
388
389
    /* Init phantom points */
390
0
    if (unlikely (!points.resize (points.length + PHANTOM_COUNT))) return false;
391
0
    hb_array_t<contour_point_t> phantoms = points.as_array ().sub_array (points.length - PHANTOM_COUNT, PHANTOM_COUNT);
392
0
    {
393
      // Duplicated code.
394
0
      int lsb = 0;
395
0
      glyf_accelerator.hmtx->get_leading_bearing_without_var_unscaled (gid, &lsb);
396
0
      int h_delta = (int) header->xMin - lsb;
397
0
      HB_UNUSED int tsb = 0;
398
0
#ifndef HB_NO_VERTICAL
399
0
      glyf_accelerator.vmtx->get_leading_bearing_without_var_unscaled (gid, &tsb);
400
0
#endif
401
0
      int v_orig  = (int) header->yMax + tsb;
402
0
      unsigned h_adv = glyf_accelerator.hmtx->get_advance_without_var_unscaled (gid);
403
0
      unsigned v_adv =
404
0
#ifndef HB_NO_VERTICAL
405
0
                       glyf_accelerator.vmtx->get_advance_without_var_unscaled (gid)
406
#else
407
                       - font->face->get_upem ()
408
#endif
409
0
                       ;
410
0
      phantoms[PHANTOM_LEFT].x = h_delta;
411
0
      phantoms[PHANTOM_RIGHT].x = (int) h_adv + h_delta;
412
0
      phantoms[PHANTOM_TOP].y = v_orig;
413
0
      phantoms[PHANTOM_BOTTOM].y = v_orig - (int) v_adv;
414
0
    }
415
416
0
#ifndef HB_NO_VAR
417
0
    if (hb_any (coords))
418
0
    {
419
0
      if (!glyf_accelerator.gvar->apply_deltas_to_points (gid,
420
0
                coords,
421
0
                points.as_array ().sub_array (old_length),
422
0
                scratch,
423
0
                gvar_cache,
424
0
                phantom_only && type == SIMPLE,
425
0
                budget))
426
0
        return false;
427
0
    }
428
0
#endif
429
430
    // mainly used by CompositeGlyph calculating new X/Y offset value so no need to extend it
431
    // with child glyphs' points
432
0
    if (points_with_deltas != nullptr && depth == 0 && type == COMPOSITE)
433
0
    {
434
0
      assert (old_length == 0);
435
0
      *points_with_deltas = points;
436
0
    }
437
438
0
    float shift = 0;
439
0
    switch (type) {
440
0
    case SIMPLE:
441
0
      if (depth == 0 && head_maxp_info)
442
0
        head_maxp_info->maxPoints = hb_max (head_maxp_info->maxPoints, all_points.length - old_length - 4);
443
0
      shift = phantoms[PHANTOM_LEFT].x;
444
0
      break;
445
0
    case COMPOSITE:
446
0
    {
447
0
      hb_decycler_node_t decycler_node (scratch.decycler);
448
449
0
      unsigned int comp_index = 0;
450
0
      for (auto &item : get_composite_iterator ())
451
0
      {
452
0
  hb_codepoint_t item_gid = item.get_gid (extended);
453
454
0
        if (unlikely (!decycler_node.visit (item_gid)))
455
0
  {
456
0
    comp_index++;
457
0
    continue;
458
0
  }
459
460
0
  unsigned old_count = all_points.length;
461
462
0
  if (unlikely ((!phantom_only || (use_my_metrics && item.is_use_my_metrics ())) &&
463
0
          !glyf_accelerator.glyph_for_gid (item_gid)
464
0
               .get_points (font,
465
0
                glyf_accelerator,
466
0
                all_points,
467
0
                scratch,
468
0
                points_with_deltas,
469
0
                head_maxp_info,
470
0
                composite_contours,
471
0
                shift_points_hori,
472
0
                use_my_metrics,
473
0
                phantom_only,
474
0
                coords,
475
0
                gvar_cache,
476
0
                depth + 1,
477
0
                edge_count,
478
0
                budget)))
479
0
  {
480
0
    points.resize (old_length);
481
0
    return false;
482
0
  }
483
484
  // points might have been reallocated. Relocate phantoms.
485
0
  phantoms = points.as_array ().sub_array (points.length - PHANTOM_COUNT, PHANTOM_COUNT);
486
487
0
  auto comp_points = all_points.as_array ().sub_array (old_count);
488
489
  /* Copy phantom points from component if USE_MY_METRICS flag set */
490
0
  if (use_my_metrics && item.is_use_my_metrics ())
491
0
    for (unsigned int i = 0; i < PHANTOM_COUNT; i++)
492
0
      phantoms[i] = comp_points[comp_points.length - PHANTOM_COUNT + i];
493
494
  /* Each composite level transforms the whole subtree below it;
495
   * charge that work here so deep nesting is bounded by the budget
496
   * rather than by the per-draw point cap alone. */
497
0
  if (unlikely (!hb_budget_spend (*budget, HB_BUDGET_1, comp_points.length)))
498
0
  {
499
0
    points.resize (old_length);
500
0
    return false;
501
0
  }
502
503
0
  if (comp_points) // Empty in case of phantom_only
504
0
  {
505
0
    float matrix[4];
506
0
    contour_point_t default_trans;
507
0
    item.get_transformation (matrix, default_trans, extended);
508
509
    /* Apply component transformation & translation (with deltas applied) */
510
0
    item.transform_points (comp_points, matrix, points[old_length + comp_index]);
511
0
  }
512
513
0
  if (item.is_anchored () && !phantom_only)
514
0
  {
515
0
    unsigned int p1, p2;
516
0
    item.get_anchor_points (p1, p2, extended);
517
0
    if (likely (p1 < all_points.length && p2 < comp_points.length))
518
0
    {
519
0
      contour_point_t delta;
520
0
      delta.init (all_points[p1].x - comp_points[p2].x,
521
0
      all_points[p1].y - comp_points[p2].y);
522
523
0
      item.translate (delta, comp_points);
524
0
    }
525
0
  }
526
527
0
  all_points.resize (all_points.length - PHANTOM_COUNT);
528
529
0
  if (all_points.length > HB_GLYF_MAX_POINTS)
530
0
  {
531
0
    points.resize (old_length);
532
0
    return false;
533
0
  }
534
535
0
  comp_index++;
536
0
      }
537
538
0
      if (head_maxp_info && depth == 0)
539
0
      {
540
0
        if (composite_contours)
541
0
          head_maxp_info->maxCompositeContours = hb_max (head_maxp_info->maxCompositeContours, *composite_contours);
542
0
        head_maxp_info->maxCompositePoints = hb_max (head_maxp_info->maxCompositePoints, all_points.length);
543
0
        head_maxp_info->maxComponentElements = hb_max (head_maxp_info->maxComponentElements, comp_index);
544
0
      }
545
0
      all_points.extend (phantoms);
546
0
      shift = phantoms[PHANTOM_LEFT].x;
547
0
      points.resize (old_length);
548
0
    } break;
549
0
    case EMPTY:
550
0
      all_points.extend (phantoms);
551
0
      shift = phantoms[PHANTOM_LEFT].x;
552
0
      points.resize (old_length);
553
0
      break;
554
0
    }
555
556
0
    if (depth == 0 && shift_points_hori) /* Apply at top level */
557
0
    {
558
      /* Undocumented rasterizer behavior:
559
       * Shift points horizontally by the updated left side bearing
560
       */
561
0
      if (shift)
562
0
        for (auto &point : all_points)
563
0
    point.x -= shift;
564
0
    }
565
566
0
    return !all_points.in_error ();
567
0
  }
568
569
  bool get_extents_without_var_scaled (hb_font_t *font, const glyf_accelerator_t &glyf_accelerator,
570
               hb_glyph_extents_t *extents) const
571
0
  {
572
0
    if (type == EMPTY)
573
0
    {
574
0
      *extents = {0, 0, 0, 0};
575
0
      return true; /* Empty glyph; zero extents. */
576
0
    }
577
0
    return header->get_extents_without_var_scaled (font, glyf_accelerator, gid, extents);
578
0
  }
579
580
0
  hb_bytes_t get_bytes () const { return bytes; }
581
0
  glyph_type_t get_type () const { return type; }
582
0
  const GlyphHeader *get_header () const { return header; }
583
0
  bool is_extended () const { return extended; }
584
585
0
  Glyph () : bytes (),
586
0
             header (bytes.as<GlyphHeader> ()),
587
0
             gid (-1),
588
0
             type(EMPTY),
589
0
       extended (false)
590
0
  {}
591
592
  Glyph (hb_bytes_t bytes_,
593
   hb_codepoint_t gid_,
594
0
   bool extended_) : bytes (bytes_),
595
0
         header (bytes.as<GlyphHeader> ()),
596
0
         gid (gid_),
597
0
         extended (extended_)
598
0
  {
599
0
    int num_contours = header->numberOfContours;
600
0
    if (unlikely (num_contours == 0)) type = EMPTY;
601
0
    else if (num_contours > 0) type = SIMPLE;
602
0
    else if (num_contours <= -1) type = COMPOSITE;
603
0
    else type = EMPTY; // Spec deviation; Spec says COMPOSITE, but not seen in the wild.
604
0
  }
605
606
  protected:
607
  hb_bytes_t bytes;
608
  const GlyphHeader *header;
609
  hb_codepoint_t gid;
610
  glyph_type_t type;
611
  bool extended;
612
};
613
614
615
} /* namespace glyf_impl */
616
} /* namespace OT */
617
618
619
#endif /* OT_GLYF_GLYPH_HH */